<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<meta name="theme-color" content="#222"><meta name="generator" content="Hexo 5.4.0">


  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png">
  <link rel="mask-icon" href="/images/logo.svg" color="#222">

<link rel="stylesheet" href="/css/main.css">



<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css" integrity="sha256-AbA177XfpSnFEvgpYu1jMygiLabzPCJCRIBtR5jGc0k=" crossorigin="anonymous">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.1.1/animate.min.css" integrity="sha256-PR7ttpcvz8qrF57fur/yAx1qXMFJeJFiA6pSzWi0OIE=" crossorigin="anonymous">

<script class="next-config" data-name="main" type="application/json">{"hostname":"tomstand.gitee.io","root":"/","images":"/images","scheme":"Muse","darkmode":false,"version":"8.13.0","exturl":false,"sidebar":{"position":"left","display":"post","padding":18,"offset":12},"copycode":{"enable":false,"style":null},"bookmark":{"enable":false,"color":"#222","save":"auto"},"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"stickytabs":false,"motion":{"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"fadeInDown","post_body":"fadeInDown","coll_header":"fadeInLeft","sidebar":"fadeInUp"}},"prism":false,"i18n":{"placeholder":"Searching...","empty":"We didn't find any results for the search: ${query}","hits_time":"${hits} results found in ${time} ms","hits":"${hits} results found"}}</script><script src="/js/config.js"></script>

    <meta property="og:type" content="website">
<meta property="og:title" content="猿日记">
<meta property="og:url" content="https://tomstand.gitee.io/index.html">
<meta property="og:site_name" content="猿日记">
<meta property="og:locale" content="en_US">
<meta property="article:author" content="tomstand">
<meta name="twitter:card" content="summary">


<link rel="canonical" href="https://tomstand.gitee.io/">



<script class="next-config" data-name="page" type="application/json">{"sidebar":"","isHome":true,"isPost":false,"lang":"en","comments":"","permalink":"","path":"index.html","title":""}</script>

<script class="next-config" data-name="calendar" type="application/json">""</script>
<title>猿日记</title>
  






  <noscript>
    <link rel="stylesheet" href="/css/noscript.css">
  </noscript>
</head>

<body itemscope itemtype="http://schema.org/WebPage" class="use-motion">
  <div class="headband"></div>

  <main class="main">
    <header class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="Toggle navigation bar" role="button">
    </div>
  </div>

  <div class="site-meta">

    <a href="/" class="brand" rel="start">
      <i class="logo-line"></i>
      <h1 class="site-title">猿日记</h1>
      <i class="logo-line"></i>
    </a>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger">
    </div>
  </div>
</div>







</div>
        
  
  <div class="toggle sidebar-toggle" role="button">
    <span class="toggle-line"></span>
    <span class="toggle-line"></span>
    <span class="toggle-line"></span>
  </div>

  <aside class="sidebar">

    <div class="sidebar-inner sidebar-overview-active">
      <ul class="sidebar-nav">
        <li class="sidebar-nav-toc">
          Table of Contents
        </li>
        <li class="sidebar-nav-overview">
          Overview
        </li>
      </ul>

      <div class="sidebar-panel-container">
        <!--noindex-->
        <div class="post-toc-wrap sidebar-panel">
        </div>
        <!--/noindex-->

        <div class="site-overview-wrap sidebar-panel">
          <div class="site-author site-overview-item animated" itemprop="author" itemscope itemtype="http://schema.org/Person">
  <p class="site-author-name" itemprop="name">tomstand</p>
  <div class="site-description" itemprop="description"></div>
</div>
<div class="site-state-wrap site-overview-item animated">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
        <a href="/archives/">
          <span class="site-state-item-count">12</span>
          <span class="site-state-item-name">posts</span>
        </a>
      </div>
  </nav>
</div>



        </div>
      </div>
    </div>
  </aside>
  <div class="sidebar-dimmer"></div>


    </header>

    
  <div class="back-to-top" role="button" aria-label="Back to top">
    <i class="fa fa-arrow-up"></i>
    <span>0%</span>
  </div>

<noscript>
  <div class="noscript-warning">Theme NexT works best with JavaScript enabled</div>
</noscript>


    <div class="main-inner index posts-expand">

    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="https://tomstand.gitee.io/2023/11/21/%E5%9F%BA%E4%BA%8Enode%E7%9A%84%E8%B7%A8%E5%B9%B3%E5%8F%B0%E6%8A%93%E5%8C%85%E4%BB%A3%E7%90%86%E5%B7%A5%E5%85%B7whistle/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="tomstand">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="猿日记">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | 猿日记">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2023/11/21/%E5%9F%BA%E4%BA%8Enode%E7%9A%84%E8%B7%A8%E5%B9%B3%E5%8F%B0%E6%8A%93%E5%8C%85%E4%BB%A3%E7%90%86%E5%B7%A5%E5%85%B7whistle/" class="post-title-link" itemprop="url">Untitled</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">Posted on</span>

      <time title="Created: 2023-11-21 20:16:19" itemprop="dateCreated datePublished" datetime="2023-11-21T20:16:19+08:00">2023-11-21</time>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h2 id="基于node的跨平台抓包代理工具whistle"><a href="#基于node的跨平台抓包代理工具whistle" class="headerlink" title="基于node的跨平台抓包代理工具whistle"></a>基于node的跨平台抓包代理工具whistle</h2><blockquote>
<p>详细内容请进入官网查看 <a target="_blank" rel="noopener" href="http://wproxy.org/whistle/">http://wproxy.org/whistle/</a></p>
</blockquote>
<ol>
<li><p>安装Node</p>
<p>whistle支持<code>v0.10.0</code>以上版本的Node，为获取更好的性能，推荐安装最新版本的Node。</p>
<ul>
<li>Windows或Mac系统，访问<a target="_blank" rel="noopener" href="https://nodejs.org/%EF%BC%8C%E5%AE%89%E8%A3%85**LTS**%E7%89%88%E6%9C%AC%E7%9A%84Node%EF%BC%8C%E9%BB%98%E8%AE%A4%E5%AE%89%E8%A3%85%E5%8D%B3%E5%8F%AF">https://nodejs.org/，安装**LTS**版本的Node，默认安装即可</a></li>
<li>Linux下推荐使用源码安装: 从<a target="_blank" rel="noopener" href="https://nodejs.org/en/download/">Node官网</a>下载最新版的<strong>Source Code</strong>(或者用<code>wget</code>命令下载)，解压文件(<code>tar -xzvf node-vx.y.z.tar.gz</code>)后进入解压后的根目录(<code>node-vx.y.z</code>)，依次执行<code>./configure</code>、<code>./make</code>和<code>./make install</code>。</li>
</ul>
</li>
<li><p>安装whistle</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install -g whistle</span><br></pre></td></tr></table></figure>

<p>npm默认镜像是在国外，有时候安装速度很慢或者出现安装不了的情况，如果无法安装或者安装很慢，可以使用taobao的镜像安装：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">$ npm install cnpm -g --registry=https://registry.npm.taobao.org</span><br><span class="line">$ cnpm install -g whistle</span><br><span class="line"></span><br><span class="line">或者直接指定镜像安装：</span><br><span class="line">$ npm install whistle -g --registry=https://registry.npm.taobao.org</span><br></pre></td></tr></table></figure></li>
<li><p>启动whistle</p>
<blockquote>
<p>最新版本的whistle支持三种等价的命令<code>whistle</code>、<code>w2</code>、<code>wproxy</code></p>
</blockquote>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">启动whistle</span><br><span class="line">w2 restart</span><br><span class="line">停止whistle</span><br><span class="line">w2 stop</span><br><span class="line">调试模式</span><br><span class="line">w2 run</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>启动后默认端口是8899，如果需要切换端口可以加<code>-p</code>切换 根据日志访问对应网址就可以显示具体的数据<br><img src="https://img-blog.csdnimg.cn/img_convert/d7fd4517ce4e6b894a865e0b8a60e170.png" alt="image-20201210162331306.png"></p>
</li>
</ol>
<ol start="4">
<li>移动端抓包可以点击https栏目扫描二维码安装证书就可以抓到移动端的请求记录了</li>
</ol>
<p> <img src="https://img-blog.csdnimg.cn/img_convert/171c93dd8e977700850137ac62831cc7.png" alt="image-20201210162931997.png"></p>
<ol start="5">
<li><p>浏览器抓包推荐使用google浏览器下载插件</p>
<p>百度云盘链接：<a target="_blank" rel="noopener" href="https://pan.baidu.com/s/10LPdgM2sIiihDakV8xokoA">https://pan.baidu.com/s/10LPdgM2sIiihDakV8xokoA</a><br>提取码：vq3z </p>
<p>下载后再谷歌插件库中导入即可，开启代理就可以拦截请求</p>
</li>
</ol>
<p><img src="https://img-blog.csdnimg.cn/img_convert/8af790aa23bb8772cbad34fa2bbc132b.png" alt="image-20201210164150432.png"></p>
<ol start="6">
<li>还可以做域名代理，将官网域名代理到本地的服务中解决测试跨域的问题</li>
</ol>
<p><img src="https://img-blog.csdnimg.cn/img_convert/5c21de207a8f46ca3ae52815c698537c.png" alt="image-20201210164536231.png"></p>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="https://tomstand.gitee.io/2023/11/21/%E5%A4%A7%E6%95%B0%E6%8D%AE%E6%8A%80%E6%9C%AF%E6%A0%88/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="tomstand">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="猿日记">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | 猿日记">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2023/11/21/%E5%A4%A7%E6%95%B0%E6%8D%AE%E6%8A%80%E6%9C%AF%E6%A0%88/" class="post-title-link" itemprop="url">Untitled</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">Posted on</span>

      <time title="Created: 2023-11-21 20:16:19" itemprop="dateCreated datePublished" datetime="2023-11-21T20:16:19+08:00">2023-11-21</time>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="大数据技术栈"><a href="#大数据技术栈" class="headerlink" title="大数据技术栈"></a>大数据技术栈</h1><blockquote>
<p>提起大数据，不得不提由IBM提出的关于大数据的5V特点：Volume（大量）、Velocity（高速）、Variety（多样）、Value（低价值密度）、Veracity（真实性），而对于大数据领域的从业人员的日常工作也与这5V密切相关。大数据技术在过去的几十年中取得非常迅速的发展，尤以Hadoop和Spark最为突出，已构建起庞大的技术生态体系圈</p>
</blockquote>
<p>首先通过一张图来了解一下目前大数据领域常用的一些技术，当然大数据发展至今所涉及技术远不止这些。</p>
<p><img src="https://img-blog.csdnimg.cn/img_convert/82d037350a8ffbed74dcd97f1489a8d5.png" alt="image-20210326145020065"></p>
<h2 id="数据采集和传输层"><a href="#数据采集和传输层" class="headerlink" title="数据采集和传输层"></a>数据采集和传输层</h2><h3 id="Flume"><a href="#Flume" class="headerlink" title="Flume"></a>Flume</h3><blockquote>
<p>是一个分布式的、可靠的、高可用的用于数据采集、聚合和传输的系统。常用于日志采集系统中，支持定制各类数据发送方用于收集数据、通过自定义拦截器对数据进行简单的预处理并传输到各种数据接收方如HDFS、HBase、Kafka中。之前由Cloudera开发，后纳入apache</p>
</blockquote>
<h3 id="Logstash"><a href="#Logstash" class="headerlink" title="Logstash"></a>Logstash</h3><blockquote>
<p>ELK工作栈的一员，也常用于数据采集，是开源的服务器端数据处理管道</p>
</blockquote>
<h3 id="Sqoop"><a href="#Sqoop" class="headerlink" title="Sqoop"></a>Sqoop</h3><blockquote>
<p>Sqoop主要通过一组命令进行数据导入导出的工具，底层引擎依赖于MapReduce，主要用于Hadoop（如HDFS、Hive、HBase）和RDBMS（如mysql、oracle）之间的数据导入导出</p>
</blockquote>
<h3 id="Kafka"><a href="#Kafka" class="headerlink" title="Kafka"></a>Kafka</h3><blockquote>
<p> 分布式消息系统。生产者（producer）——消费者（consumer）模型。提供了类似于JMS的特性，但设计上完全不同，不遵循JMS规范。如kafka允许多个消费者主动拉取数据，而JMS中只有点对点模式消费者才会主动拉取数据。主要应用在数据缓冲、异步通信、汇集数据、系统接偶等方面</p>
</blockquote>
<h3 id="Pulsar"><a href="#Pulsar" class="headerlink" title="Pulsar"></a>Pulsar</h3><blockquote>
<p>pub-sub模式的分布式消息平台，拥有灵活的消息模型和直观的客户端API。类似于Kafka，但Pulsar支持多租户，有着资产和命名空间的概念，资产代表系统里的租户。假设有一个Pulsar集群用于支持多个应用程序，集群里的每个资产可以代表一个组织的团队、一个核心的功能或一个产品线。一个资产可以包含多个命名空间，一个命名空间可以包含任意个主题</p>
</blockquote>
<h2 id="数据存储层"><a href="#数据存储层" class="headerlink" title="数据存储层"></a>数据存储层</h2><h3 id="HBase"><a href="#HBase" class="headerlink" title="HBase"></a>HBase</h3><blockquote>
<p> 基于Google Bigtable的开源实现，是一个具有高可靠性、高性能、面向列、可伸缩性、典型的key/value分布式存储的nosql数据库系统，主要用于海量结构化和半结构化数据存储。它介于nosql和RDBMS之间，仅能通过行键（row key）和行键的range来检索数据，行数据存储是原子性的，仅支持单行事务（可通过hive支持来实现多表join等复杂操作）。HBase查询数据功能很简单，不支持join等复杂操作，不支持跨行和跨表事务</p>
</blockquote>
<h3 id="Kudu"><a href="#Kudu" class="headerlink" title="Kudu"></a>Kudu</h3><blockquote>
<p>介于HDFS和HBase之间的基于列式存储的分布式数据库。兼具了HBase的实时性、HDFS的高吞吐，以及传统数据库的sql支持</p>
</blockquote>
<h3 id="HDFS"><a href="#HDFS" class="headerlink" title="HDFS"></a>HDFS</h3><blockquote>
<p>分布式文件存储系统，具有高容错（high fault-tolerant）、高吞吐（high throughput）、高可用（high available）的特性。HDFS非常适合大规模数据集上的应用，提供高吞吐量的数据访问，可部署在廉价的机器上。它放宽了POSIX的要求，这样可以实现流的形式访问（文件系统中的数据。主要为各类分布式计算框架如Spark、MapReduce等提供海量数据存储服务，同时HDFS和HBase底层数据存储也依赖于HDFS</p>
</blockquote>
<h2 id="数据分析层"><a href="#数据分析层" class="headerlink" title="数据分析层"></a>数据分析层</h2><h3 id="Spark"><a href="#Spark" class="headerlink" title="Spark"></a>Spark</h3><blockquote>
<p>Spark是一个快速、通用、可扩展、可容错的、内存迭代式计算的大数据分析引擎。目前生态体系主要包括用于批数据处理的SparkRDD、SparkSQL，用于流数据处理的SparkStreaming、Structured-Streaming，用于机器学习的Spark MLLib，用于图计算的Graphx以及用于统计分析的SparkR，支持Java、Scala、Python、R多种数据语言</p>
</blockquote>
<h3 id="Flink"><a href="#Flink" class="headerlink" title="Flink"></a>Flink</h3><blockquote>
<p>分布式的大数据处理引擎，可以对有限数据流和无线数据流进行有状态的计算。Flink在设计之初就是以流为基础发展的，然后再进入批处理领域，相对于spark而言，它是一个真正意义上的实时计算引擎</p>
</blockquote>
<h3 id="Storm"><a href="#Storm" class="headerlink" title="Storm"></a>Storm</h3><blockquote>
<p> 由Twitter开源后归于Apache管理的分布式实时计算系统。Storm是一个没有批处理能力的数据流处理计算引擎，storm提供了偏底层的API，用户需要自己实现很多复杂的逻辑</p>
</blockquote>
<h3 id="MapReduce"><a href="#MapReduce" class="headerlink" title="MapReduce"></a>MapReduce</h3><blockquote>
<p>分布式运算程序的编程框架，适用于离线数据处理场景，内部处理流程主要划分map和reduce两个阶段</p>
</blockquote>
<h3 id="Hive"><a href="#Hive" class="headerlink" title="Hive"></a>Hive</h3><blockquote>
<p>Hive是基于Hadoop的一个数据仓库工具，可以将结构化的数据文件映射为一张数据库表，并提供HQL语句（类SQL语言）查询功能，存储依赖于HDFS。支持多种计算引擎，如Spark、MapReduce（默认）、Tez；支持多种存储格式，如TextFile、SequenceFile、RCFile、ORC、Parquet（常用）；支持多种压缩格式，如gzip、lzo、snappy（常用）、bzip2</p>
</blockquote>
<h3 id="Tez"><a href="#Tez" class="headerlink" title="Tez"></a>Tez</h3><p>支持DAG作业的开源计算框架。相对于MapReduce性能更好，主要原因在于其将作业描述为DAG（有向无环图），这一点与Spark类似</p>
<h3 id="Pig"><a href="#Pig" class="headerlink" title="Pig"></a>Pig</h3><blockquote>
<p>基于Hadoop的大规模数据分析平台，它包含了一种名为Pig Latin的脚本语言来描述数据流，并行地执行数据流处理的引擎，为复杂的海量数据并行计算提供了一个简单的操作和编程接口。Pig Latin本身提供了许多传统的数据操作，同时允许用户自己开发一些自定义函数用来读取、处理和写数据，该语言的编译器会把类SQL的数据分析请求转换为一系列经过优化处理的MapReduce运算</p>
</blockquote>
<h3 id="Mahout"><a href="#Mahout" class="headerlink" title="Mahout"></a>Mahout</h3><blockquote>
<p>提供一些可扩展的机器学习领域经典算法的实现，Mahout包含许多实现，包括聚类、分类、推荐过滤、频繁子项挖掘。通过使用Apache Hadoop 库，可以将Mahout扩展到云中</p>
</blockquote>
<h3 id="Phoenix"><a href="#Phoenix" class="headerlink" title="Phoenix"></a>Phoenix</h3><blockquote>
<p>构建在HBase之上的一个SQL层，能让我们通过标准的JDBC API操作HBase中的数据。Phoenix完全使用Java编写，作为HBase内嵌的JDBC驱动。Phoenix查询引擎会将SQL查询转换为一个或多个HBase scan，并编排执行以生成标准JDBC结果集</p>
</blockquote>
<h2 id="OLAP引擎"><a href="#OLAP引擎" class="headerlink" title="OLAP引擎"></a>OLAP引擎</h2><h3 id="Druid"><a href="#Druid" class="headerlink" title="Druid"></a>Druid</h3><blockquote>
<p>开源的、基于列存储的、分布式的，适用于实时数据分析的存储系统，能够快速聚合、灵活过滤、毫秒级查询和低延迟数据导入。通过使用Bitmap indexing加速列存储的查询速度，并使用CONCISE算法来对bitmap indexing进行压缩，使得生成的segments比原始文本文件小很多，并且它的各个组成部分之间耦合性低，如果不需要实时数据完全可以忽略实时节点</p>
</blockquote>
<h3 id="Kylin"><a href="#Kylin" class="headerlink" title="Kylin"></a>Kylin</h3><blockquote>
<p>最初由eBayInc.开发并贡献至开源社区的分布式分析引擎。提供Hadoop/Spark之上的SQL查询接口及多维分析（OLAP）能力以支持超大规模数据，它能在亚秒内查询巨大的Hive表。需要使用者对数仓模型有深度了解，并需构建cube。能够与多种可视化工具，如Tableau，PowerBI等，令用户可以使用BI工具对Hadoop数据进行分析</p>
</blockquote>
<h3 id="Impala"><a href="#Impala" class="headerlink" title="Impala"></a>Impala</h3><blockquote>
<p>提供对HDFS、HBase等数据的高性能、低延迟的交互式SQL查询功能的大数据查询分析引擎，由Cloudera开源。它基于Hive，使用Hive的元数据在内存中计算，具有实时、批处理、高并发等优点</p>
</blockquote>
<h3 id="Presto"><a href="#Presto" class="headerlink" title="Presto"></a>Presto</h3><blockquote>
<p>开源的分布式大数据SQL查询引擎，适用于交互式分析查询。可以将多个数据源的数据进行合并，并且可以直接从HDFS读取数据，在使用前不需要大量的ETL操作</p>
</blockquote>
<h2 id="资源管理层"><a href="#资源管理层" class="headerlink" title="资源管理层"></a>资源管理层</h2><h3 id="Yarn"><a href="#Yarn" class="headerlink" title="Yarn"></a>Yarn</h3><blockquote>
<p>Yarn是一个资源调度平台，负责为运算程序分配资源和调度，不参与用户程序内部工作。核心组件包括：ResourceManager（全局资源管理器，负责整个系统的资源管理和分配）、NodeManager（每个节点上的资源和任务管理器）</p>
</blockquote>
<h3 id="Kubernetes"><a href="#Kubernetes" class="headerlink" title="Kubernetes"></a>Kubernetes</h3><blockquote>
<p>又称K8s，为容器化的应用提供资源调度、部署运行、均衡容灾、服务注册、扩容缩容等功能的自动化容器操作的开源平台。具体体现在：自动化容器的部署和复制、随时扩展或收缩容器规模、将容器组织成组，并且提供容器间的负载均衡等。Kubernetes支持docker和Rocket，可以将Docker看成Kubernetes内部使用的低级别组件</p>
</blockquote>
<h3 id="Mesos"><a href="#Mesos" class="headerlink" title="Mesos"></a>Mesos</h3><blockquote>
<p>类似于Yarn，也是一个分布式资源管理平台，为MPI、Spark作业在统一资源管理环境下运行。它对Hadoop2.0支持很好，但国内用的不多</p>
</blockquote>
<h2 id="工作流调度器"><a href="#工作流调度器" class="headerlink" title="工作流调度器"></a>工作流调度器</h2><h3 id="Oozie"><a href="#Oozie" class="headerlink" title="Oozie"></a>Oozie</h3><blockquote>
<p>基于工作流引擎的任务调度框架，能够提供能够提供对MapReduce和Pig 任务的调度与协调</p>
</blockquote>
<h3 id="Azkaban"><a href="#Azkaban" class="headerlink" title="Azkaban"></a>Azkaban</h3><blockquote>
<p>由LinkedIn开源，相对Oozie更轻量级。用于在一个工作流内以一个特定顺序运行一组任务，通过一种kv文件格式来建立任务之间的依赖关系并为用户提供了易于使用的web界面来维护和跟踪允许任务的工作流</p>
</blockquote>
<h2 id="其他"><a href="#其他" class="headerlink" title="其他"></a>其他</h2><h3 id="Ambari"><a href="#Ambari" class="headerlink" title="Ambari"></a>Ambari</h3><blockquote>
<p>基于web的安装部署工具，支持对大多数的Hadoop组件，如HDFS、MapReduce、Hive、Pig、HBase等的管理和监控</p>
</blockquote>
<h3 id="Zookeeper"><a href="#Zookeeper" class="headerlink" title="Zookeeper"></a>Zookeeper</h3><blockquote>
<p>分布式协调服务即为用户的分布式应用程序提供协调服务，如主从协调、服务器节点动态上下线、统一配置管理、分布式共享锁等，它本身也是一个分布式程序（部署奇数台，只要由半数以上zookeeper节点存活，zookeeper集群就能正常提供服务），它是Google Chubby一个开源实现</p>
</blockquote>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="https://tomstand.gitee.io/2023/11/21/%E5%88%86%E5%B8%83%E5%BC%8F%E9%80%9A%E4%BF%A1%E5%8D%8F%E8%AE%AE%E5%88%86%E6%9E%90-TCPIP/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="tomstand">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="猿日记">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | 猿日记">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2023/11/21/%E5%88%86%E5%B8%83%E5%BC%8F%E9%80%9A%E4%BF%A1%E5%8D%8F%E8%AE%AE%E5%88%86%E6%9E%90-TCPIP/" class="post-title-link" itemprop="url">Untitled</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">Posted on</span>

      <time title="Created: 2023-11-21 20:16:19" itemprop="dateCreated datePublished" datetime="2023-11-21T20:16:19+08:00">2023-11-21</time>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="TCP-IP"><a href="#TCP-IP" class="headerlink" title="TCP/IP"></a>TCP/IP</h1><blockquote>
<p>TCP/IP（Transmission Control Protocol/Internet Protocol）是一种可靠的网络数据传输控制协议。定义了主机如何连入因特网以及数据如何在他们之间传输的标准。</p>
</blockquote>
<p>TCP/IP协议参考模型把所有TCP/IP系列协议归类到四个抽象层中；<br>每一个抽象层建立在低一层提供的服务上，并且为高一层提供服务</p>
<p><img src="https://img-blog.csdnimg.cn/img_convert/31debdcecf635fea61960c9ce619bef7.png" alt="image.png"></p>
<p>ICMP：控制报文协议<br>IGMP：internet组管理协议<br>ARP：地址解析协议<br>RARP：反向地址转化协议</p>
<blockquote>
<p>OSI模型（开放式系统互联通信参考模型），它是由国际化标准组织提出来的，试图使各种计算机在世界范围内互联为网络的标准框架</p>
</blockquote>
<p>OSI模型多出了表达层和会话层。</p>
<h4 id="三次握手协议"><a href="#三次握手协议" class="headerlink" title="三次握手协议"></a>三次握手协议</h4><p>所谓三次握手（Three-Way Handshake）即建立TCP连接，就是指建立一个TCP连接时，需要客户端和服务端总共发送三个包以确认连接的建立</p>
<p><img src="https://img-blog.csdnimg.cn/img_convert/e36a7323e8cf3c684448b0e0272d8a23.png" alt="suo"></p>
<ol>
<li>第一次握手：Client将标志位SYN置为1，随机产生一个seq=J，并将该数据包发送给Server，Client进入SYN_SENT状态，等待Server确认。</li>
<li>第二次握手：Server收到数据包后由标志位SYN=1知道Client请求建立连接，Server将标志位SYN和ACK都置为1，ack=J+1，随机产生一个值seq=K，并将该数据包发送给Client以确认连接请求，Server进入SYN_RCVD状态。</li>
<li>第三次握手：Client收到确认后，检查ack是否为J+1，ACK是否为1，如果正确则将标志位ACK置为1，ack=K+1，并将该数据包发送给Server，Server检查ack是否为K+1，ACK是否为1，如果正确则连接建立成功，Client和Server进入ESTABLISHED状态，完成三次握手，随后Client和Server之间可以开始传输数据了。</li>
</ol>
<blockquote>
<p>SYN攻击：<br>    在三次握手过程中，Server发送SYN-ACK之后，收到Client的ACK之前的TCP连接称为半连接（half-open connect），此时Server处于SYN_RCVD状态，当收到ACK后，Server转入ESTABLISHED状态。SYN攻击就是Client在短时间内伪造不存在的IP地址，并向Server不断地发送SYN包，Server回复确认包，并等待Client的确认，由于原地址是不存在的，因此，Server需要不断重发直至超时，这些伪造的SYN包将产时间占用未连接队列，导致正常的SYN请求因为队列慢而被丢弃，从而引起网络堵塞甚至系统瘫痪。SYN攻击是一种典型的DDOS攻击，检测SYN攻击的方式非常简单，即当Server上有大量半连接状态且源IP地址是随机的，则可以断定遭到SYN攻击了，使用如下命令可以让之现行：<br> <code>#netstat -nap | grep   SYN_RECV</code></p>
</blockquote>
<h4 id="四次挥手协议"><a href="#四次挥手协议" class="headerlink" title="四次挥手协议"></a>四次挥手协议</h4><p>三次握手耳熟能详，四次挥手估计就听得比较少了，所谓四次挥手（Four-Way Wavehand）即终止TCP连接，就是指断开一个TCP连接时，需要客户端和服务端总共发送4个包以确认连接的断开.</p>
<ul>
<li>单工：数据传输只支持数据在一个方向上传输</li>
<li>半双工：数据传输允许数据在两个方向上传输，但是在某一时刻，只允许在一个方向上传输，实际上有点像切换方向的单工通信。</li>
<li>全双工：数据通信允许数据同时在两个方向上传输，因此全双工是两个单工通信方式的结合，它要求发送设备和接收设备都有独立的接收和发送能力</li>
</ul>
<p><img src="https://img-blog.csdnimg.cn/img_convert/da89c5652f32bb4edd1f1b72f9a2a294.png" alt="image.png"></p>
<p>由于TCP连接是全双工的，因此，每个方向都必须要单独进行关闭，这一原则是当一方完成数据发送任务后，发送一个FIN来终止这一方向的连接，收到一个FIN只是意味着这一方向上没有数据流动了，即不会再收到数据了，但是在这个TCP连接上仍然能够发送数据，直到这一方向也发送了FIN.首先进行关闭的一方将执行主动关闭，而另一方则执行被动关闭，上图描述的即是如此。</p>
<ol>
<li>第一次挥手：Client发送了一个FIN，用来关闭Client到Server的数据传送，Client进入FIN_WAIT_1状态。</li>
<li>第二次挥手：Server收到FIN后，发送一个ACK给Client，确认序号为收到序号+1（与SYN相同，一个FIN占用一个序号），Server进入CLOSE_WAIT状态。</li>
<li>第三次挥手：Server发送一个FIN，用来关闭Server到Client的数据传送，Server进入LAST_ACK状态。</li>
<li>第四次挥手：Client收到FIN后，Client进入TIME_WAIT状态，接着发送一个ACK给Server，确认序号为收到序号+1，Server进入CLOSED状态，完成四次握手。</li>
</ol>
<h5 id="TCP通信原理"><a href="#TCP通信原理" class="headerlink" title="TCP通信原理"></a>TCP通信原理</h5><ul>
<li>首先，对于TCP通信来说，每个TCP  Socket的内核都有一个发送缓冲区和一个接收缓冲区，TCP的全双工的工作模式及TCP的滑动窗口就是依赖于这两个独立的Buffer和该Buffer的填充状态。</li>
<li>接收缓冲区就是把数据缓冲到内核，若应用进程一直没有调用Socket的read方法进行读取，那么该数据会一直被缓存在接收缓冲区内。不管进程是否读取Socket，对端发来的数据都会经过内核接收并缓存到Socket的内核接收缓冲区。</li>
<li>read所要做的工作，就是把内核接收缓冲区的数据复制到应用层用户的Buffer里。</li>
<li>进程调用Socket的send发送数据的时候，一般情况下是将数据从应用层用户的Buffer里复制到Socket的内核发送缓冲区，然后send就会在上层返回。换句话说，send返回时，数据不一定会被发送到对端.</li>
</ul>
<p><img src="https://img-blog.csdnimg.cn/img_convert/599ac44e5f09b7c30f24da0ba8ad8de5.png" alt="image.png"></p>
<p><img src="https://img-blog.csdnimg.cn/img_convert/441574642ce97472afbd981783080c9f.png" alt="image.png"></p>
<h4 id="滑动窗口协议"><a href="#滑动窗口协议" class="headerlink" title="滑动窗口协议"></a>滑动窗口协议</h4><ul>
<li>发送方和接收方都会维护一个数据帧的序列，这个序列被称作窗口。发送方的窗口大小由接收方确认，目的是控制发送速度，以免接收方的缓存不够大导致溢出，同时控制流量也可以避免网络拥塞。</li>
<li>下面图中的4,5,6号数据帧已经被发送出去，但是未收到关联的ACK，7,8,9帧则是等待发送。可以看出发送端的窗口大小为6，这是由接受端告知的（事实上必须考虑拥塞窗口cwnd，这里暂且考虑cwnd&gt;rwnd）。此时如果发送端收到4号ACK，则窗口的左边缘向右收缩，窗口的右边缘则向右扩展，此时窗口就向前“滑动了”，即数据帧10也可以被发送</li>
</ul>
<p><img src="https://img-blog.csdnimg.cn/img_convert/5d14d09b21dbae9a1807ca6c7b25d104.png" alt="image.png"></p>
<ul>
<li>明白了Socket读写数据的底层原理，我们就很容易理解“阻塞模式”：对于读取Socket数据的过程而言，如果接收缓冲区为空，则调用Socket的read方法的线程会阻塞，直到有数据进入接收缓冲区；而对于写数据到Socket中的线程来说，如果待发送的数据长度大于发送缓冲区空余长度，则会阻塞在write方法上，等待发送缓冲区的报文被发送到网络上，然后继续发送下一段数据，循环上述过程直到数据都被写入到发送缓冲区为止。</li>
<li>从前面分析的过程中，传统的Socket阻塞模式直接导致每个Socket都必须绑定一个线程来操作数据，参与通信的任意一方如果处理数据的速度较慢，会直接拖累到另一方，导致另一方的线程不得不浪费大量的时间在I/O等待上，所以这就是Socket阻塞模式的缺陷。但是这种模式在少量的TCP连接通信的情况下，双方都可以快速的传输数据，这个时候的性能是最高的。</li>
</ul>
<p><img src="https://img-blog.csdnimg.cn/img_convert/ca61ed380f513433d05fa07b6e84e796.png" alt="image.png"></p>
<p><img src="https://img-blog.csdnimg.cn/img_convert/f3370bd222e8ceb1c98002a311acedd8.png" alt="image.png"></p>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="https://tomstand.gitee.io/2023/11/21/%E5%88%86%E5%B8%83%E5%BC%8F%E7%BC%93%E5%AD%98%E6%8A%80%E6%9C%AF-redis/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="tomstand">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="猿日记">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | 猿日记">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2023/11/21/%E5%88%86%E5%B8%83%E5%BC%8F%E7%BC%93%E5%AD%98%E6%8A%80%E6%9C%AF-redis/" class="post-title-link" itemprop="url">Untitled</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">Posted on</span>

      <time title="Created: 2023-11-21 20:16:19" itemprop="dateCreated datePublished" datetime="2023-11-21T20:16:19+08:00">2023-11-21</time>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h2 id="分布式缓存技术-redis"><a href="#分布式缓存技术-redis" class="headerlink" title="分布式缓存技术-redis"></a>分布式缓存技术-redis</h2><h3 id="缓存的用途"><a href="#缓存的用途" class="headerlink" title="缓存的用途"></a>缓存的用途</h3><ul>
<li><p>高性能：redis是基于内存进行操作的，性能较高，前端发送请求后，后端会查询数据库，中间加入redis后可以将数据先存在redis中，下次读取的时候会直接读取redis的数据性能就会提高</p>
</li>
<li><p>高并发：mysql的数据库<strong>每秒查询率（QPS）是2000</strong>，redis的上限就比较大，单点下<strong>系统吞吐量（TPS）是8W/S</strong> ，<strong>每秒查询率（QPS）是10W</strong> ，redis集群情况下根式可以支持每秒几十万的读写并发处理，这样应对高并发场景就可以满足要求</p>
</li>
</ul>
<h3 id="Redis的线程模型"><a href="#Redis的线程模型" class="headerlink" title="Redis的线程模型"></a>Redis的线程模型</h3><p>Redis内部使用文件事件处理  <code>file event handler</code>  ，这个文件事件处理器是单线程的，所以Redis才叫做单线程的模型。它采用IO多路复用机制同时监听多个socket，将产生的socket压入内存队列中，事件分排气根据socket上的事件类型来选择对应的时间处理器进行处理。</p>
<p>文件事件处理器的结构包含 4 个部分：</p>
<ul>
<li>多个 socket</li>
<li>IO 多路复用程序</li>
<li>文件事件分派器</li>
<li>事件处理器（连接应答处理器、命令请求处理器、命令回复处理器）</li>
</ul>
<p>多个 socket 可能会并发产生不同的操作，每个操作对应不同的文件事件，但是 IO 多路复用程序会监听多个 socket，会将产生事件的 socket 放入队列中排队，事件分派器每次从队列中取出一个 socket，根据 socket 的事件类型交给对应的事件处理器进行处理。</p>
<p><img src="https://img-blog.csdnimg.cn/img_convert/88379a880bd8d84dd4e96fe538555d48.png" alt="image-20210115160039283"></p>
<p>要明白，通信是通过 socket 来完成的，不懂的同学可以先去看一看 socket 网络编程。</p>
<p>首先，Redis 服务端进程初始化的时候，会将 server socket 的 <code>AE_READABLE</code> 事件与连接应答处理器关联。</p>
<p>客户端 socket01 向 Redis 进程的 server socket 请求建立连接，此时 server socket 会产生一个 <code>AE_READABLE</code> 事件，IO 多路复用程序监听到 server socket 产生的事件后，将该 socket 压入队列中。文件事件分派器从队列中获取 socket，交给<strong>连接应答处理器</strong>。连接应答处理器会创建一个能与客户端通信的 socket01，并将该 socket01 的 <code>AE_READABLE</code> 事件与命令请求处理器关联。</p>
<p>假设此时客户端发送了一个 <code>set key value</code> 请求，此时 Redis 中的 socket01 会产生 <code>AE_READABLE</code> 事件，IO 多路复用程序将 socket01 压入队列，此时事件分派器从队列中获取到 socket01 产生的 <code>AE_READABLE</code> 事件，由于前面 socket01 的 <code>AE_READABLE</code> 事件已经与命令请求处理器关联，因此事件分派器将事件交给命令请求处理器来处理。命令请求处理器读取 socket01 的 <code>key value</code> 并在自己内存中完成 <code>key value</code> 的设置。操作完成后，它会将 socket01 的 <code>AE_WRITABLE</code> 事件与命令回复处理器关联。</p>
<p>如果此时客户端准备好接收返回结果了，那么 Redis 中的 socket01 会产生一个 <code>AE_WRITABLE</code> 事件，同样压入队列中，事件分派器找到相关联的命令回复处理器，由命令回复处理器对 socket01 输入本次操作的一个结果，比如 <code>ok</code> ，之后解除 socket01 的 <code>AE_WRITABLE</code> 事件与命令回复处理器的关联。</p>
<h3 id="Redis的数据类型和使用场景"><a href="#Redis的数据类型和使用场景" class="headerlink" title="Redis的数据类型和使用场景"></a>Redis的数据类型和使用场景</h3><p>Redis 主要有以下几种数据类型：</p>
<ul>
<li>Strings：这是最简单的类型，就是普通的 set 和 get，做简单的 KV 缓存。</li>
<li>Hashes：这个是类似 map 的一种结构，这个一般就是可以将结构化的数据，比如一个对象（前提是<strong>这个对象没嵌套其他的对象</strong>）给缓存在 Redis 里，然后每次读写缓存的时候，可以就操作 hash 里的<strong>某个字段</strong>。</li>
<li>Lists：有序列表，比如可以通过 list 存储一些列表型的数据结构，类似粉丝列表、文章的评论列表之类的东西。比如可以搞个简单的消息队列，从 list 头怼进去，从 list 尾巴那里弄出来。</li>
<li>Sets：无序集合，自动去重。</li>
<li>Sorted Sets：排序的 set，去重但可以排序，写进去的时候给一个分数，自动根据分数排序。</li>
</ul>
<blockquote>
<p>Redis 除了这 5 种数据类型之外，还有 Bitmaps、HyperLogLogs、Streams 等。</p>
</blockquote>
<h3 id="Redis-过期策略"><a href="#Redis-过期策略" class="headerlink" title="Redis 过期策略"></a>Redis 过期策略</h3><p>Redis 过期策略是：<strong>定期删除+惰性删除</strong>。</p>
<p>所谓<strong>定期删除</strong>，指的是 Redis 默认是每隔 100ms 就随机抽取一些设置了过期时间的 key，检查其是否过期，如果过期就删除。</p>
<p>假设 Redis 里放了 10w 个 key，都设置了过期时间，你每隔几百毫秒，就检查 10w 个 key，那 Redis 基本上就死了，cpu 负载会很高的，消耗在你的检查过期 key 上了。注意，这里可不是每隔 100ms 就遍历所有的设置过期时间的 key，那样就是一场性能上的<strong>灾难</strong>。实际上 Redis 是每隔 100ms <strong>随机抽取</strong>一些 key 来检查和删除的。</p>
<p>但是问题是，定期删除可能会导致很多过期 key 到了时间并没有被删除掉，那咋整呢？所以就是惰性删除了。这就是说，在你获取某个 key 的时候，Redis 会检查一下 ，这个 key 如果设置了过期时间那么是否过期了？如果过期了此时就会删除，不会给你返回任何东西。</p>
<blockquote>
<p>获取 key 的时候，如果此时 key 已经过期，就删除，不会返回任何东西。</p>
</blockquote>
<p>但是实际上这还是有问题的，如果定期删除漏掉了很多过期 key，然后你也没及时去查，也就没走惰性删除，此时会怎么样？如果大量过期 key 堆积在内存里，导致 Redis 内存块耗尽了，咋整？</p>
<p>答案是：<strong>走内存淘汰机制</strong>。</p>
<h3 id="内存淘汰机制"><a href="#内存淘汰机制" class="headerlink" title="内存淘汰机制"></a>内存淘汰机制</h3><p>Redis 内存淘汰机制有以下几个：</p>
<ul>
<li>noeviction: 当内存不足以容纳新写入数据时，新写入操作会报错，这个一般没人用吧，实在是太恶心了。</li>
<li><strong>allkeys-lru</strong>：当内存不足以容纳新写入数据时，在<strong>键空间</strong>中，移除最近最少使用的 key（这个是<strong>最常用</strong>的）。</li>
<li>allkeys-random：当内存不足以容纳新写入数据时，在<strong>键空间</strong>中，随机移除某个 key，这个一般没人用吧，为啥要随机，肯定是把最近最少使用的 key 给干掉啊。</li>
<li>volatile-lru：当内存不足以容纳新写入数据时，在<strong>设置了过期时间的键空间</strong>中，移除最近最少使用的 key（这个一般不太合适）。</li>
<li>volatile-random：当内存不足以容纳新写入数据时，在<strong>设置了过期时间的键空间</strong>中，<strong>随机移除</strong>某个 key。</li>
<li>volatile-ttl：当内存不足以容纳新写入数据时，在<strong>设置了过期时间的键空间</strong>中，有<strong>更早过期时间</strong>的 key 优先移除。</li>
</ul>
<h3 id="Redis的雪崩、穿透、击穿"><a href="#Redis的雪崩、穿透、击穿" class="headerlink" title="Redis的雪崩、穿透、击穿"></a>Redis的雪崩、穿透、击穿</h3><h4 id="缓存雪崩"><a href="#缓存雪崩" class="headerlink" title="缓存雪崩"></a>缓存雪崩</h4><blockquote>
<p>缓存雪崩指在某段时间内缓存服务器意外宕机导致所有数据直接查数据库，数据库压力大导致数据库崩溃</p>
</blockquote>
<p>缓存雪崩的事前事中事后的解决方案如下：</p>
<ul>
<li>事前：Redis 高可用，主从+哨兵，Redis cluster，避免全盘崩溃。</li>
<li>事中：本地 ehcache 缓存 + hystrix 限流&amp;降级，避免 MySQL 被打死。</li>
<li>事后：Redis 持久化，一旦重启，自动从磁盘上加载数据，快速恢复缓存数据。</li>
</ul>
<h4 id="缓存穿透"><a href="#缓存穿透" class="headerlink" title="缓存穿透"></a>缓存穿透</h4><blockquote>
<p>缓存穿透指请求的某个参数在缓存和数据库中都不存在会一直直接查数据库，导致数据库压力大崩溃</p>
</blockquote>
<p>缓存穿透解决方案是每次系统从数据库没查到数据就写一个默认值，并设置 一个比较短的过期时间，这样的话，下次有相同的 key 来访问的时候，在缓存失效之前，都可以直接从缓存中取数据。</p>
<h4 id="缓存击穿"><a href="#缓存击穿" class="headerlink" title="缓存击穿"></a>缓存击穿</h4><blockquote>
<p>缓存击穿是某个热点key访问频繁，在高峰期缓存失效瞬间大量请求击穿缓存访问数据库，导致数据库压力大崩溃</p>
</blockquote>
<p>不同场景下的解决方式可如下：</p>
<ul>
<li>若缓存的数据是基本不会发生更新的，则可尝试将该热点数据设置为永不过期。</li>
<li>若缓存的数据更新不频繁，且缓存刷新的整个流程耗时较少的情况下，则可以采用基于 Redis、zookeeper 等分布式中间件的分布式互斥锁，或者本地互斥锁以保证仅少量的请求能请求数据库并重新构建缓存，其余线程则在锁释放后能访问到新缓存。</li>
<li>若缓存的数据更新频繁或者在缓存刷新的流程耗时较长的情况下，可以利用定时线程在缓存过期前主动地重新构建缓存或者延后缓存的过期时间，以保证所有的请求能一直访问到对应的缓存。</li>
</ul>
<h3 id="Redis的持久化策略"><a href="#Redis的持久化策略" class="headerlink" title="Redis的持久化策略"></a>Redis的持久化策略</h3><p>redis的常用持久化方式有两种：</p>
<ul>
<li>RDB：RDB 持久化机制，是对 Redis 中的数据执行<strong>周期性</strong>的持久化。</li>
<li>AOF：AOF 机制对每条写入命令作为日志，以 <code>append-only</code> 的模式写入一个日志文件中，在 Redis 重启的时候，可以通过<strong>回放</strong> AOF 日志中的写入指令来重新构建整个数据集。</li>
</ul>
<h4 id="RDB-优缺点"><a href="#RDB-优缺点" class="headerlink" title="RDB 优缺点"></a>RDB 优缺点</h4><ul>
<li>RDB 会生成多个数据文件，每个数据文件都代表了某一个时刻中 Redis 的数据，这种多个数据文件的方式，<strong>非常适合做冷备</strong>，可以将这种完整的数据文件发送到一些远程的安全存储上去，比如说 Amazon 的 S3 云服务上去，在国内可以是阿里云的 ODPS 分布式存储上，以预定好的备份策略来定期备份 Redis 中的数据。</li>
<li>RDB 对 Redis 对外提供的读写服务，影响非常小，可以让 Redis <strong>保持高性能</strong>，因为 Redis 主进程只需要 fork 一个子进程，让子进程执行磁盘 IO 操作来进行 RDB 持久化即可。</li>
<li>相对于 AOF 持久化机制来说，直接基于 RDB 数据文件来重启和恢复 Redis 进程，更加快速。</li>
<li>如果想要在 Redis 故障时，尽可能少的丢失数据，那么 RDB 没有 AOF 好。一般来说，RDB 数据快照文件，都是每隔 5 分钟，或者更长时间生成一次，这个时候就得接受一旦 Redis 进程宕机，那么会丢失最近 5 分钟的数据。</li>
<li>RDB 每次在 fork 子进程来执行 RDB 快照数据文件生成的时候，如果数据文件特别大，可能会导致对客户端提供的服务暂停数毫秒，或者甚至数秒。</li>
</ul>
<h4 id="AOF-优缺点"><a href="#AOF-优缺点" class="headerlink" title="AOF 优缺点"></a>AOF 优缺点</h4><ul>
<li>AOF 可以更好的保护数据不丢失，一般 AOF 会每隔 1 秒，通过一个后台线程执行一次 <code>fsync</code> 操作，最多丢失 1 秒钟的数据。</li>
<li>AOF 日志文件以 <code>append-only</code> 模式写入，所以没有任何磁盘寻址的开销，写入性能非常高，而且文件不容易破损，即使文件尾部破损，也很容易修复。</li>
<li>AOF 日志文件即使过大的时候，出现后台重写操作，也不会影响客户端的读写。因为在 <code>rewrite</code> log 的时候，会对其中的指令进行压缩，创建出一份需要恢复数据的最小日志出来。在创建新日志文件的时候，老的日志文件还是照常写入。当新的 merge 后的日志文件 ready 的时候，再交换新老日志文件即可。</li>
<li>AOF 日志文件的命令通过可读较强的方式进行记录，这个特性非常<strong>适合做灾难性的误删除的紧急恢复</strong>。比如某人不小心用 <code>flushall</code> 命令清空了所有数据，只要这个时候后台 <code>rewrite</code> 还没有发生，那么就可以立即拷贝 AOF 文件，将最后一条 <code>flushall</code> 命令给删了，然后再将该 <code>AOF</code> 文件放回去，就可以通过恢复机制，自动恢复所有数据。</li>
<li>对于同一份数据来说，AOF 日志文件通常比 RDB 数据快照文件更大。</li>
<li>AOF 开启后，支持的写 QPS 会比 RDB 支持的写 QPS 低，因为 AOF 一般会配置成每秒 <code>fsync</code> 一次日志文件，当然，每秒一次 <code>fsync</code> ，性能也还是很高的。（如果实时写入，那么 QPS 会大降，Redis 性能会大大降低）</li>
<li>以前 AOF 发生过 bug，就是通过 AOF 记录的日志，进行数据恢复的时候，没有恢复一模一样的数据出来。所以说，类似 AOF 这种较为复杂的基于命令日志 / merge / 回放的方式，比基于 RDB 每次持久化一份完整的数据快照文件的方式，更加脆弱一些，容易有 bug。不过 AOF 就是为了避免 rewrite 过程导致的 bug，因此每次 rewrite 并不是基于旧的指令日志进行 merge 的，而是<strong>基于当时内存中的数据进行指令的重新构建</strong>，这样健壮性会好很多。</li>
</ul>
<h4 id="RDB-和-AOF-到底该如何选择"><a href="#RDB-和-AOF-到底该如何选择" class="headerlink" title="RDB 和 AOF 到底该如何选择"></a>RDB 和 AOF 到底该如何选择</h4><ul>
<li>不要仅仅使用 RDB，因为那样会导致你丢失很多数据；</li>
<li>也不要仅仅使用 AOF，因为那样有两个问题：第一，你通过 AOF 做冷备，没有 RDB 做冷备来的恢复速度更快；第二，RDB 每次简单粗暴生成数据快照，更加健壮，可以避免 AOF 这种复杂的备份和恢复机制的 bug；</li>
<li>Redis 支持同时开启开启两种持久化方式，我们可以综合使用 AOF 和 RDB 两种持久化机制，用 AOF 来保证数据不丢失，作为数据恢复的第一选择; 用 RDB 来做不同程度的冷备，在 AOF 文件都丢失或损坏不可用的时候，还可以使用 RDB 来进行快速的数据恢复。</li>
</ul>
<h4 id="QA-两种方式同时开启时，系统会有线选择AOF方式恢复数据"><a href="#QA-两种方式同时开启时，系统会有线选择AOF方式恢复数据" class="headerlink" title="QA: 两种方式同时开启时，系统会有线选择AOF方式恢复数据"></a>QA: 两种方式同时开启时，系统会有线选择AOF方式恢复数据</h4><h3 id="缓存和数据库的一致性处理方案"><a href="#缓存和数据库的一致性处理方案" class="headerlink" title="缓存和数据库的一致性处理方案"></a>缓存和数据库的一致性处理方案</h3><blockquote>
<p>一般来说，如果允许缓存可以稍微的跟数据库偶尔有不一致的情况，也就是说如果你的系统<strong>不是严格要求</strong> “缓存+数据库” 必须保持一致性的话，最好不要做这个方案，即：<strong>读请求和写请求串行化</strong>，串到一个<strong>内存队列</strong>里去。</p>
<p>串行化可以保证一定不会出现不一致的情况，但是它也会导致系统的吞吐量大幅度降低，用比正常情况下多几倍的机器去支撑线上的一个请求。</p>
</blockquote>
<h4 id="Cache-Aside-Pattern"><a href="#Cache-Aside-Pattern" class="headerlink" title="Cache Aside Pattern"></a>Cache Aside Pattern</h4><p>最经典的缓存+数据库读写的模式，就是 Cache Aside Pattern。</p>
<ul>
<li>读的时候，先读缓存，缓存没有的话，就读数据库，然后取出数据后放入缓存，同时返回响应。</li>
<li>更新的时候，<strong>先更新数据库，然后再删除缓存</strong>。</li>
</ul>
<p><strong>为什么是删除缓存，而不是更新缓存？</strong></p>
<p>原因很简单，很多时候，在复杂点的缓存场景，缓存不单单是数据库中直接取出来的值。</p>
<p>比如可能更新了某个表的一个字段，然后其对应的缓存，是需要查询另外两个表的数据并进行运算，才能计算出缓存最新的值的。</p>
<p>另外更新缓存的代价有时候是很高的。是不是说，每次修改数据库的时候，都一定要将其对应的缓存更新一份？也许有的场景是这样，但是对于<strong>比较复杂的缓存数据计算的场景</strong>，就不是这样了。如果你频繁修改一个缓存涉及的多个表，缓存也频繁更新。但是问题在于，<strong>这个缓存到底会不会被频繁访问到？</strong></p>
<p>举个栗子，一个缓存涉及的表的字段，在 1 分钟内就修改了 20 次，或者是 100 次，那么缓存更新 20 次、100 次；但是这个缓存在 1 分钟内只被读取了 1 次，有<strong>大量的冷数据</strong>。实际上，如果你只是删除缓存的话，那么在 1 分钟内，这个缓存不过就重新计算一次而已，开销大幅度降低。<strong>用到缓存才去算缓存。</strong></p>
<p>其实删除缓存，而不是更新缓存，就是一个 lazy 计算的思想，不要每次都重新做复杂的计算，不管它会不会用到，而是让它到需要被使用的时候再重新计算。像 mybatis，hibernate，都有懒加载思想。查询一个部门，部门带了一个员工的 list，没有必要说每次查询部门，都把里面的 1000 个员工的数据也同时查出来啊。80% 的情况，查这个部门，就只是要访问这个部门的信息就可以了。先查部门，同时要访问里面的员工，那么这个时候只有在你要访问里面的员工的时候，才会去数据库里面查询 1000 个员工。</p>
<h4 id="最初级的缓存不一致问题及解决方案"><a href="#最初级的缓存不一致问题及解决方案" class="headerlink" title="最初级的缓存不一致问题及解决方案"></a>最初级的缓存不一致问题及解决方案</h4><p><strong>Q</strong>：先更新数据库，再删除缓存。如果删除缓存失败了，那么会导致数据库中是新数据，缓存中是旧数据，数据就出现了不一致。</p>
<p><strong>A</strong>：先删除缓存，再更新数据库。如果数据库更新失败了，那么数据库中是旧数据，缓存中是空的，那么数据不会不一致。因为读的时候缓存没有，所以去读了数据库中的旧数据，然后更新到缓存中</p>
<h4 id="比较复杂的数据不一致问题分析"><a href="#比较复杂的数据不一致问题分析" class="headerlink" title="比较复杂的数据不一致问题分析"></a>比较复杂的数据不一致问题分析</h4><p><strong>Q</strong>：数据发生了变更，先删除了缓存，然后要去修改数据库，此时还没修改。一个请求过来，去读缓存，发现缓存空了，去查询数据库，<strong>查到了修改前的旧数据</strong>，放到了缓存中。随后数据变更的程序完成了数据库的修改。完了，数据库和缓存中的数据不一样了…</p>
<p><strong>为什么上亿流量高并发场景下，缓存会出现这个问题？</strong></p>
<p>只有在对一个数据在并发的进行读写的时候，才可能会出现这种问题。其实如果说你的并发量很低的话，特别是读并发很低，每天访问量就 1 万次，那么很少的情况下，会出现刚才描述的那种不一致的场景。但是问题是，如果每天的是上亿的流量，每秒并发读是几万，每秒只要有数据更新的请求，就<strong>可能会出现上述的数据库+缓存不一致的情况</strong>。</p>
<p><strong>A</strong>：更新数据的时候，根据<strong>数据的唯一标识</strong>，将操作路由之后，发送到一个 jvm 内部队列中。读取数据的时候，如果发现数据不在缓存中，那么将重新执行“读取数据+更新缓存”的操作，根据唯一标识路由之后，也发送到同一个 jvm 内部队列中。</p>
<p>一个队列对应一个工作线程，每个工作线程<strong>串行</strong>拿到对应的操作，然后一条一条的执行。这样的话，一个数据变更的操作，先删除缓存，然后再去更新数据库，但是还没完成更新。此时如果一个读请求过来，没有读到缓存，那么可以先将缓存更新的请求发送到队列中，此时会在队列中积压，然后同步等待缓存更新完成。</p>
<p>这里有一个<strong>优化点</strong>，一个队列中，其实<strong>多个更新缓存请求串在一起是没意义的</strong>，因此可以做过滤，如果发现队列中已经有一个更新缓存的请求了，那么就不用再放个更新请求操作进去了，直接等待前面的更新操作请求完成即可。</p>
<p>待那个队列对应的工作线程完成了上一个操作的数据库的修改之后，才会去执行下一个操作，也就是缓存更新的操作，此时会从数据库中读取最新的值，然后写入缓存中。</p>
<p>如果请求还在等待时间范围内，不断轮询发现可以取到值了，那么就直接返回；如果请求等待的时间超过一定时长，那么这一次直接从数据库中读取当前的旧值。</p>
<h4 id="高并发场景下遇到的问题"><a href="#高并发场景下遇到的问题" class="headerlink" title="高并发场景下遇到的问题"></a>高并发场景下遇到的问题</h4><p>高并发的场景下，该解决方案要注意的问题：</p>
<ul>
<li>读请求长时阻塞</li>
</ul>
<p>由于读请求进行了非常轻度的异步化，所以一定要注意读超时的问题，每个读请求必须在超时时间范围内返回。</p>
<p>该解决方案，最大的风险点在于说，<strong>可能数据更新很频繁</strong>，导致队列中积压了大量更新操作在里面，然后<strong>读请求会发生大量的超时</strong>，最后导致大量的请求直接走数据库。务必通过一些模拟真实的测试，看看更新数据的频率是怎样的。</p>
<p>另外一点，因为一个队列中，可能会积压针对多个数据项的更新操作，因此需要根据自己的业务情况进行测试，可能需要<strong>部署多个服务</strong>，每个服务分摊一些数据的更新操作。如果一个内存队列里居然会挤压 100 个商品的库存修改操作，每个库存修改操作要耗费 10ms 去完成，那么最后一个商品的读请求，可能等待 10 * 100 = 1000ms = 1s 后，才能得到数据，这个时候就导致<strong>读请求的长时阻塞</strong>。</p>
<p>一定要做根据实际业务系统的运行情况，去进行一些压力测试，和模拟线上环境，去看看最繁忙的时候，内存队列可能会挤压多少更新操作，可能会导致最后一个更新操作对应的读请求，会 hang 多少时间，如果读请求在 200ms 返回，如果你计算过后，哪怕是最繁忙的时候，积压 10 个更新操作，最多等待 200ms，那还可以的。</p>
<p><strong>如果一个内存队列中可能积压的更新操作特别多</strong>，那么你就要<strong>加机器</strong>，让每个机器上部署的服务实例处理更少的数据，那么每个内存队列中积压的更新操作就会越少。</p>
<p>其实根据之前的项目经验，一般来说，数据的写频率是很低的，因此实际上正常来说，在队列中积压的更新操作应该是很少的。像这种针对读高并发、读缓存架构的项目，一般来说写请求是非常少的，每秒的 QPS 能到几百就不错了。</p>
<p>我们来<strong>实际粗略测算一下</strong>。</p>
<p>如果一秒有 500 的写操作，如果分成 5 个时间片，每 200ms 就 100 个写操作，放到 20 个内存队列中，每个内存队列，可能就积压 5 个写操作。每个写操作性能测试后，一般是在 20ms 左右就完成，那么针对每个内存队列的数据的读请求，也就最多 hang 一会儿，200ms 以内肯定能返回了。</p>
<p>经过刚才简单的测算，我们知道，单机支撑的写 QPS 在几百是没问题的，如果写 QPS 扩大了 10 倍，那么就扩容机器，扩容 10 倍的机器，每个机器 20 个队列。</p>
<ul>
<li>读请求并发量过高</li>
</ul>
<p>这里还必须做好压力测试，确保恰巧碰上上述情况的时候，还有一个风险，就是突然间大量读请求会在几十毫秒的延时 hang 在服务上，看服务能不能扛的住，需要多少机器才能扛住最大的极限情况的峰值。</p>
<p>但是因为并不是所有的数据都在同一时间更新，缓存也不会同一时间失效，所以每次可能也就是少数数据的缓存失效了，然后那些数据对应的读请求过来，并发量应该也不会特别大。</p>
<ul>
<li>多服务实例部署的请求路由</li>
</ul>
<p>可能这个服务部署了多个实例，那么必须<strong>保证</strong>说，执行数据更新操作，以及执行缓存更新操作的请求，都通过 Nginx 服务器<strong>路由到相同的服务实例上</strong>。</p>
<p>比如说，对同一个商品的读写请求，全部路由到同一台机器上。可以自己去做服务间的按照某个请求参数的 hash 路由，也可以用 Nginx 的 hash 路由功能等等。</p>
<ul>
<li>热点商品的路由问题，导致请求的倾斜</li>
</ul>
<p>万一某个商品的读写请求特别高，全部打到相同的机器的相同的队列里面去了，可能会造成某台机器的压力过大。就是说，因为只有在商品数据更新的时候才会清空缓存，然后才会导致读写并发，所以其实要根据业务系统去看，如果更新频率不是太高的话，这个问题的影响并不是特别大，但是的确可能某些机器的负载会高一些。</p>
<h3 id="Redis的集群方案"><a href="#Redis的集群方案" class="headerlink" title="Redis的集群方案"></a>Redis的集群方案</h3><ul>
<li><p>主从高可用：该方案就是单实例形式，只是为了保证数据的安全，对于用户数据少，业务的前期可以采用</p>
</li>
<li><p>客户端分片： 典型代表：Jedis。自主写分片算法，代码掌握在自己手中，可控性强，但是需要专业的开发运维人员维护，技术要求和维护成本高</p>
</li>
<li><p>代理分片： 典型代表：Twemproxy，redis集群没有正式推出之前官网推荐的方案，也是目前使用最多的</p>
</li>
<li><p>Redis cluster ： redis3版本以后推出的版本，也是官网推荐的版本</p>
</li>
<li><p> Codis集群：豌豆荚15年开源的解决方案，开源之前其已经用了2年之多，与其同期官网推出redis cluster</p>
</li>
</ul>
<h3 id="Redis-主从架构"><a href="#Redis-主从架构" class="headerlink" title="Redis 主从架构"></a>Redis 主从架构</h3><p>单机的 Redis，能够承载的 QPS 大概就在上万到几万不等。对于缓存来说，一般都是用来支撑<strong>读高并发</strong>的。因此架构做成主从(master-slave)架构，一主多从，主负责写，并且将数据复制到其它的 slave 节点，从节点负责读。所有的<strong>读请求全部走从节点</strong>。这样也可以很轻松实现水平扩容，<strong>支撑读高并发</strong>。</p>
<p>Redis replication -&gt; 主从架构 -&gt; 读写分离 -&gt; 水平扩容支撑读高并发</p>
<h4 id="Redis-replication-的核心机制"><a href="#Redis-replication-的核心机制" class="headerlink" title="Redis replication 的核心机制"></a>Redis replication 的核心机制</h4><ul>
<li>Redis 采用<strong>异步方式</strong>复制数据到 slave 节点，不过 Redis2.8 开始，slave node 会周期性地确认自己每次复制的数据量；</li>
<li>一个 master node 是可以配置多个 slave node 的；</li>
<li>slave node 也可以连接其他的 slave node；</li>
<li>slave node 做复制的时候，不会 block master node 的正常工作；</li>
<li>slave node 在做复制的时候，也不会 block 对自己的查询操作，它会用旧的数据集来提供服务；但是复制完成的时候，需要删除旧数据集，加载新数据集，这个时候就会暂停对外服务了；</li>
<li>slave node 主要用来进行横向扩容，做读写分离，扩容的 slave node 可以提高读的吞吐量。</li>
</ul>
<p>注意，如果采用了主从架构，那么建议必须<strong>开启</strong> master node 的<a target="_blank" rel="noopener" href="https://github.com/tomstand/advanced-java/blob/master/docs/high-concurrency/redis-persistence.md">持久化</a>，不建议用 slave node 作为 master node 的数据热备，因为那样的话，如果你关掉 master 的持久化，可能在 master 宕机重启的时候数据是空的，然后可能一经过复制， slave node 的数据也丢了。</p>
<p>另外，master 的各种备份方案，也需要做。万一本地的所有文件丢失了，从备份中挑选一份 rdb 去恢复 master，这样才能<strong>确保启动的时候，是有数据的</strong>，即使采用了后续讲解的<a target="_blank" rel="noopener" href="https://github.com/tomstand/advanced-java/blob/master/docs/high-concurrency/redis-sentinel.md">高可用机制</a>，slave node 可以自动接管 master node，但也可能 sentinel 还没检测到 master failure，master node 就自动重启了，还是可能导致上面所有的 slave node 数据被清空。</p>
<h4 id="Redis-主从复制的核心原理"><a href="#Redis-主从复制的核心原理" class="headerlink" title="Redis 主从复制的核心原理"></a>Redis 主从复制的核心原理</h4><p>当启动一个 slave node 的时候，它会发送一个 <code>PSYNC</code> 命令给 master node。</p>
<p>如果这是 slave node 初次连接到 master node，那么会触发一次 <code>full resynchronization</code> 全量复制。此时 master 会启动一个后台线程，开始生成一份 <code>RDB</code> 快照文件，同时还会将从客户端 client 新收到的所有写命令缓存在内存中。 <code>RDB</code> 文件生成完毕后， master 会将这个 <code>RDB</code> 发送给 slave，slave 会先<strong>写入本地磁盘，然后再从本地磁盘加载到内存</strong>中，接着 master 会将内存中缓存的写命令发送到 slave，slave 也会同步这些数据。slave node 如果跟 master node 有网络故障，断开了连接，会自动重连，连接之后 master node 仅会复制给 slave 部分缺少的数据。</p>
<h4 id="主从复制的断点续传"><a href="#主从复制的断点续传" class="headerlink" title="主从复制的断点续传"></a>主从复制的断点续传</h4><p>从 Redis2.8 开始，就支持主从复制的断点续传，如果主从复制过程中，网络连接断掉了，那么可以接着上次复制的地方，继续复制下去，而不是从头开始复制一份。</p>
<p>master node 会在内存中维护一个 backlog，master 和 slave 都会保存一个 replica offset 还有一个 master run id，offset 就是保存在 backlog 中的。如果 master 和 slave 网络连接断掉了，slave 会让 master 从上次 replica offset 开始继续复制，如果没有找到对应的 offset，那么就会执行一次 <code>resynchronization</code> 。</p>
<blockquote>
<p>如果根据 host+ip 定位 master node，是不靠谱的，如果 master node 重启或者数据出现了变化，那么 slave node 应该根据不同的 run id 区分。</p>
</blockquote>
<h4 id="无磁盘化复制"><a href="#无磁盘化复制" class="headerlink" title="无磁盘化复制"></a>无磁盘化复制</h4><p>master 在内存中直接创建 <code>RDB</code> ，然后发送给 slave，不会在自己本地落地磁盘了。只需要在配置文件中开启 <code>repl-diskless-sync yes</code> 即可。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">repl-diskless-sync yes</span><br><span class="line"></span><br><span class="line"># 等待 5s 后再开始复制，因为要等更多 slave 重新连接过来</span><br><span class="line">repl-diskless-sync-delay 5</span><br></pre></td></tr></table></figure>

<h4 id="过期-key-处理"><a href="#过期-key-处理" class="headerlink" title="过期 key 处理"></a>过期 key 处理</h4><p>slave 不会过期 key，只会等待 master 过期 key。如果 master 过期了一个 key，或者通过 LRU 淘汰了一个 key，那么会模拟一条 del 命令发送给 slave。</p>
<h4 id="复制的完整流程"><a href="#复制的完整流程" class="headerlink" title="复制的完整流程"></a>复制的完整流程</h4><p>slave node 启动时，会在自己本地保存 master node 的信息，包括 master node 的 <code>host</code> 和 <code>ip</code> ，但是复制流程没开始。</p>
<p>slave node 内部有个定时任务，每秒检查是否有新的 master node 要连接和复制，如果发现，就跟 master node 建立 socket 网络连接。然后 slave node 发送 <code>ping</code> 命令给 master node。如果 master 设置了 requirepass，那么 slave node 必须发送 masterauth 的口令过去进行认证。master node <strong>第一次执行全量复制</strong>，将所有数据发给 slave node。而在后续，master node 持续将写命令，异步复制给 slave node。</p>
<h4 id="全量复制"><a href="#全量复制" class="headerlink" title="全量复制"></a>全量复制</h4><ul>
<li>master 执行 bgsave ，在本地生成一份 rdb 快照文件。</li>
<li>master node 将 rdb 快照文件发送给 slave node，如果 rdb 复制时间超过 60 秒（repl-timeout），那么 slave node 就会认为复制失败，可以适当调大这个参数(对于千兆网卡的机器，一般每秒传输 100MB，6G 文件，很可能超过 60s)</li>
<li>master node 在生成 rdb 时，会将所有新的写命令缓存在内存中，在 slave node 保存了 rdb 之后，再将新的写命令复制给 slave node。</li>
<li>如果在复制期间，内存缓冲区持续消耗超过 64MB，或者一次性超过 256MB，那么停止复制，复制失败。</li>
</ul>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">client-output-buffer-limit slave 256MB 64MB 60</span><br></pre></td></tr></table></figure>

<ul>
<li>slave node 接收到 rdb 之后，清空自己的旧数据，然后重新加载 rdb 到自己的内存中，同时<strong>基于旧的数据版本</strong>对外提供服务。</li>
<li>如果 slave node 开启了 AOF，那么会立即执行 BGREWRITEAOF，重写 AOF。</li>
</ul>
<h4 id="增量复制"><a href="#增量复制" class="headerlink" title="增量复制"></a>增量复制</h4><ul>
<li>如果全量复制过程中，master-slave 网络连接断掉，那么 slave 重新连接 master 时，会触发增量复制。</li>
<li>master 直接从自己的 backlog 中获取部分丢失的数据，发送给 slave node，默认 backlog 就是 1MB。</li>
<li>master 就是根据 slave 发送的 psync 中的 offset 来从 backlog 中获取数据的。</li>
</ul>
<h4 id="heartbeat"><a href="#heartbeat" class="headerlink" title="heartbeat"></a>heartbeat</h4><p>主从节点互相都会发送 heartbeat 信息。</p>
<p>master 默认每隔 10 秒发送一次 heartbeat，slave node 每隔 1 秒发送一个 heartbeat。</p>
<h4 id="异步复制"><a href="#异步复制" class="headerlink" title="异步复制"></a>异步复制</h4><p>master 每次接收到写命令之后，先在内部写入数据，然后异步发送给 slave node。</p>
<h4 id="Redis-如何才能做到高可用"><a href="#Redis-如何才能做到高可用" class="headerlink" title="Redis 如何才能做到高可用"></a>Redis 如何才能做到高可用</h4><p>如果系统在 365 天内，有 99.99% 的时间，都是可以哗哗对外提供服务的，那么就说系统是高可用的。</p>
<p>一个 slave 挂掉了，是不会影响可用性的，还有其它的 slave 在提供相同数据下的相同的对外的查询服务。</p>
<p>但是，如果 master node 死掉了，会怎么样？没法写数据了，写缓存的时候，全部失效了。slave node 还有什么用呢，没有 master 给它们复制数据了，系统相当于不可用了。</p>
<p>Redis 的高可用架构，叫做 <code>failover</code> <strong>故障转移</strong>，也可以叫做主备切换。</p>
<p>master node 在故障时，自动检测，并且将某个 slave node 自动切换为 master node 的过程，叫做主备切换。这个过程，实现了 Redis 的主从架构下的高可用。</p>
<h3 id="Redis-Cluster"><a href="#Redis-Cluster" class="headerlink" title="Redis Cluster"></a>Redis Cluster</h3><h4 id="基本业务介绍"><a href="#基本业务介绍" class="headerlink" title="基本业务介绍"></a>基本业务介绍</h4><ul>
<li>自动将数据进行分片，每个 master 上放一部分数据</li>
<li>提供内置的高可用支持，部分 master 不可用时，还是可以继续工作的</li>
</ul>
<p>在 Redis cluster 架构下，每个 Redis 要放开两个端口号，比如一个是 6379，另外一个就是 加 1w 的端口号，比如 16379。</p>
<p>16379 端口号是用来进行节点间通信的，也就是 cluster bus 的东西，cluster bus 的通信，用来进行故障检测、配置更新、故障转移授权。cluster bus 用了另外一种二进制的协议， <code>gossip</code> 协议，用于节点间进行高效的数据交换，占用更少的网络带宽和处理时间。</p>
<h4 id="节点间的内部通信机制"><a href="#节点间的内部通信机制" class="headerlink" title="节点间的内部通信机制"></a>节点间的内部通信机制</h4><p>集群元数据的维护有两种方式：集中式、Gossip 协议。Redis cluster 节点间采用 gossip 协议进行通信</p>
<p><strong>集中式</strong>是将集群元数据（节点信息、故障等等）几种存储在某个节点上。集中式元数据集中存储的一个典型代表，就是大数据领域的 <code>storm</code> 。它是分布式的大数据实时计算引擎，是集中式的元数据存储的结构，底层基于 zookeeper（分布式协调的中间件）对所有元数据进行存储维护</p>
<p><strong>gossip</strong> 好处在于，元数据的更新比较分散，不是集中在一个地方，更新请求会陆陆续续打到所有节点上去更新，降低了压力；不好在于，元数据的更新有延时，可能导致集群中的一些操作会有一些滞后。</p>
<ul>
<li>10000 端口：每个节点都有一个专门用于节点间通信的端口，就是自己提供服务的端口号+10000，比如 7001，那么用于节点间通信的就是 17001 端口。每个节点每隔一段时间都会往另外几个节点发送 <code>ping</code> 消息，同时其它几个节点接收到 <code>ping</code> 之后返回 <code>pong</code> 。</li>
<li>交换的信息：信息包括故障信息，节点的增加和删除，hash slot 信息等等。</li>
</ul>
<h6 id="gossip-协议"><a href="#gossip-协议" class="headerlink" title="gossip 协议"></a>gossip 协议</h6><p>gossip 协议包含多种消息，包含 <code>ping</code> , <code>pong</code> , <code>meet</code> , <code>fail</code> 等等。</p>
<ul>
<li>meet：某个节点发送 meet 给新加入的节点，让新节点加入集群中，然后新节点就会开始与其它节点进行通信。</li>
</ul>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Redis-trib.rb add-node</span><br></pre></td></tr></table></figure>

<p>其实内部就是发送了一个 gossip meet 消息给新加入的节点，通知那个节点去加入我们的集群。</p>
<ul>
<li>ping：每个节点都会频繁给其它节点发送 ping，其中包含自己的状态还有自己维护的集群元数据，互相通过 ping 交换元数据。</li>
<li>pong：返回 ping 和 meeet，包含自己的状态和其它信息，也用于信息广播和更新。</li>
<li>fail：某个节点判断另一个节点 fail 之后，就发送 fail 给其它节点，通知其它节点说，某个节点宕机啦。</li>
</ul>
<h6 id="ping-消息深入"><a href="#ping-消息深入" class="headerlink" title="ping 消息深入"></a>ping 消息深入</h6><p>ping 时要携带一些元数据，如果很频繁，可能会加重网络负担。</p>
<p>每个节点每秒会执行 10 次 ping，每次会选择 5 个最久没有通信的其它节点。当然如果发现某个节点通信延时达到了 <code>cluster_node_timeout / 2</code> ，那么立即发送 ping，避免数据交换延时过长，落后的时间太长了。比如说，两个节点之间都 10 分钟没有交换数据了，那么整个集群处于严重的元数据不一致的情况，就会有问题。所以 <code>cluster_node_timeout</code> 可以调节，如果调得比较大，那么会降低 ping 的频率。</p>
<p>每次 ping，会带上自己节点的信息，还有就是带上 1/10 其它节点的信息，发送出去，进行交换。至少包含 <code>3</code> 个其它节点的信息，最多包含 <code>总节点数减 2</code> 个其它节点的信息。</p>
<h4 id="分布式寻址算法"><a href="#分布式寻址算法" class="headerlink" title="分布式寻址算法"></a>分布式寻址算法</h4><ul>
<li>hash 算法（大量缓存重建）</li>
<li>一致性 hash 算法（自动缓存迁移）+ 虚拟节点（自动负载均衡）</li>
<li>Redis cluster 的 hash slot 算法</li>
</ul>
<h5 id="hash-算法"><a href="#hash-算法" class="headerlink" title="hash 算法"></a>hash 算法</h5><p>来了一个 key，首先计算 hash 值，然后对节点数取模。然后打在不同的 master 节点上。一旦某一个 master 节点宕机，所有请求过来，都会基于最新的剩余 master 节点数去取模，尝试去取数据。这会导致<strong>大部分的请求过来，全部无法拿到有效的缓存</strong>，导致大量的流量涌入数据库。</p>
<h5 id="一致性-hash-算法"><a href="#一致性-hash-算法" class="headerlink" title="一致性 hash 算法"></a>一致性 hash 算法</h5><p>一致性 hash 算法将整个 hash 值空间组织成一个虚拟的圆环，整个空间按顺时针方向组织，下一步将各个 master 节点（使用服务器的 ip 或主机名）进行 hash。这样就能确定每个节点在其哈希环上的位置。</p>
<p>来了一个 key，首先计算 hash 值，并确定此数据在环上的位置，从此位置沿环<strong>顺时针“行走”</strong>，遇到的第一个 master 节点就是 key 所在位置。</p>
<p>在一致性哈希算法中，如果一个节点挂了，受影响的数据仅仅是此节点到环空间前一个节点（沿着逆时针方向行走遇到的第一个节点）之间的数据，其它不受影响。增加一个节点也同理。</p>
<p>燃鹅，一致性哈希算法在节点太少时，容易因为节点分布不均匀而造成<strong>缓存热点</strong>的问题。为了解决这种热点问题，一致性 hash 算法引入了虚拟节点机制，即对每一个节点计算多个 hash，每个计算结果位置都放置一个虚拟节点。这样就实现了数据的均匀分布，负载均衡。</p>
<h5 id="Redis-cluster-的-hash-slot-算法"><a href="#Redis-cluster-的-hash-slot-算法" class="headerlink" title="Redis cluster 的 hash slot 算法"></a>Redis cluster 的 hash slot 算法</h5><p>Redis cluster 有固定的 <code>16384</code> 个 hash slot，对每个 <code>key</code> 计算 <code>CRC16</code> 值，然后对 <code>16384</code> 取模，可以获取 key 对应的 hash slot。</p>
<p>Redis cluster 中每个 master 都会持有部分 slot，比如有 3 个 master，那么可能每个 master 持有 5000 多个 hash slot。hash slot 让 node 的增加和移除很简单，增加一个 master，就将其他 master 的 hash slot 移动部分过去，减少一个 master，就将它的 hash slot 移动到其他 master 上去。移动 hash slot 的成本是非常低的。客户端的 api，可以对指定的数据，让他们走同一个 hash slot，通过 <code>hash tag</code> 来实现。</p>
<p>任何一台机器宕机，另外两个节点，不影响的。因为 key 找的是 hash slot，不是机器。</p>
<h4 id="Redis-cluster-的高可用与主备切换原理"><a href="#Redis-cluster-的高可用与主备切换原理" class="headerlink" title="Redis cluster 的高可用与主备切换原理"></a>Redis cluster 的高可用与主备切换原理</h4><p>Redis cluster 的高可用的原理，几乎跟哨兵是类似的。</p>
<h5 id="判断节点宕机"><a href="#判断节点宕机" class="headerlink" title="判断节点宕机"></a>判断节点宕机</h5><p>如果一个节点认为另外一个节点宕机，那么就是 <code>pfail</code> ，<strong>主观宕机</strong>。如果多个节点都认为另外一个节点宕机了，那么就是 <code>fail</code> ，<strong>客观宕机</strong>，跟哨兵的原理几乎一样，sdown，odown。</p>
<p>在 <code>cluster-node-timeout</code> 内，某个节点一直没有返回 <code>pong</code> ，那么就被认为 <code>pfail</code> 。</p>
<p>如果一个节点认为某个节点 <code>pfail</code> 了，那么会在 <code>gossip ping</code> 消息中， <code>ping</code> 给其他节点，如果<strong>超过半数</strong>的节点都认为 <code>pfail</code> 了，那么就会变成 <code>fail</code> 。</p>
<h5 id="从节点过滤"><a href="#从节点过滤" class="headerlink" title="从节点过滤"></a>从节点过滤</h5><p>对宕机的 master node，从其所有的 slave node 中，选择一个切换成 master node。</p>
<p>检查每个 slave node 与 master node 断开连接的时间，如果超过了 <code>cluster-node-timeout * cluster-slave-validity-factor</code> ，那么就<strong>没有资格</strong>切换成 <code>master</code> 。</p>
<h5 id="从节点选举"><a href="#从节点选举" class="headerlink" title="从节点选举"></a>从节点选举</h5><p>每个从节点，都根据自己对 master 复制数据的 offset，来设置一个选举时间，offset 越大（复制数据越多）的从节点，选举时间越靠前，优先进行选举。</p>
<p>所有的 master node 开始 slave 选举投票，给要进行选举的 slave 进行投票，如果大部分 master node <code>（N/2 + 1）</code> 都投票给了某个从节点，那么选举通过，那个从节点可以切换成 master。</p>
<p>从节点执行主备切换，从节点切换为主节点。</p>
<h5 id="与哨兵比较"><a href="#与哨兵比较" class="headerlink" title="与哨兵比较"></a>与哨兵比较</h5><p>整个流程跟哨兵相比，非常类似，所以说，Redis cluster 功能强大，直接集成了 replication 和 sentinel 的功能。</p>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="https://tomstand.gitee.io/2023/11/21/%E5%88%86%E5%B8%83%E5%BC%8F%E6%90%9C%E7%B4%A2%E5%92%8C%E5%88%86%E6%9E%90%E5%BC%95%E6%93%8E-Elasticsearch/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="tomstand">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="猿日记">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | 猿日记">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2023/11/21/%E5%88%86%E5%B8%83%E5%BC%8F%E6%90%9C%E7%B4%A2%E5%92%8C%E5%88%86%E6%9E%90%E5%BC%95%E6%93%8E-Elasticsearch/" class="post-title-link" itemprop="url">Untitled</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">Posted on</span>

      <time title="Created: 2023-11-21 20:16:19" itemprop="dateCreated datePublished" datetime="2023-11-21T20:16:19+08:00">2023-11-21</time>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="分布式搜索和分析引擎-Elasticsearch"><a href="#分布式搜索和分析引擎-Elasticsearch" class="headerlink" title="分布式搜索和分析引擎-Elasticsearch"></a>分布式搜索和分析引擎-Elasticsearch</h1><blockquote>
<p><strong>Elasticsearch</strong> 是一个分布式的免费开源搜索和分析引擎，适用于包括文本、数字、地理空间、结构化和非结构化数据等在内的所有类型的数据。Elasticsearch 在Apache Lucene 的基础上开发而成，由 Elasticsearch N.V.（即现在的Elastic）于 2010 年首次发布。Elasticsearch 以其简单的 REST 风格 API、分布式特性、速度和可扩展性而闻名，是Elastic Stack 的核心组件；Elastic Stack 是一套适用于数据采集、扩充、存储、分析和可视化的免费开源工具。</p>
</blockquote>
<h2 id="应用场景"><a href="#应用场景" class="headerlink" title="应用场景"></a>应用场景</h2><ul>
<li>将搜索框添加到应用或网站</li>
<li>存储和分析日志，指标和安全事件数据</li>
<li>使用机器学习自动实时建模数据行为</li>
<li>使用Elasticsearch作为存储引擎自动化业务工作流程</li>
<li>使用Elasticsearch作为地理信息系统（GIS）管理，集成和分析空间信息</li>
<li>使用Elasticsearch作为生物信息学研究工具来存储和处理遗传数据</li>
</ul>
<h2 id="索引和文档能搜索到的场景"><a href="#索引和文档能搜索到的场景" class="headerlink" title="索引和文档能搜索到的场景"></a>索引和文档能搜索到的场景</h2><ul>
<li>区分全文字符串字段和精确值字符串字段</li>
<li>执行特定于语言的文本分析</li>
<li>优化字段以进行部分匹配</li>
<li>使用自定义日期格式</li>
<li>使用无法自动检测到的数据类型，例geo_point和geo_shape</li>
</ul>
<h2 id="倒排索引的原理"><a href="#倒排索引的原理" class="headerlink" title="倒排索引的原理"></a>倒排索引的原理</h2><blockquote>
<p>传统的索引机制是通过文章，逐个遍历找到对应关键词的位置</p>
<p><strong>倒排索引</strong>  是通过分词策略，形成了词和文章的映射关系表，这种词典+映射表就是倒排索引</p>
<p>有了倒排索引，就能实现 o（1）时间复杂度的效率检索文章了，极大的提高了</p>
<p>检索效率</p>
</blockquote>
<p><img src="https://img-blog.csdnimg.cn/img_convert/7351e5fc39fb918015ac346e79909768.png" alt="image-20210323160252906"></p>
<p>倒排索引的底层实现是基于：<strong>FST（Finite State Transducer）数据结构</strong>。</p>
<p>lucene 从 4+版本后开始大量使用的数据结构是 FST。FST 有两个优点：</p>
<p>1、空间占用小。通过对词典中单词前缀和后缀的重复利用，压缩了存储空间；</p>
<p>2、查询速度快。O(len(str))的查询时间复杂度。</p>
<p><strong>FSM(Finite State Machines) 有限状态机</strong>: 表示有限个状态（State）集合以及这些状态之间转移和动作的数学模型。其中一个状态被标记为开始状态，0个或更多的状态被标记为final状态。<br>一个FSM同一时间只处于1个状态。FSM很通用，可以用来表示多种处理过程，下面的FSM描述了《小猫咪的一天》。</p>
<p><img src="https://img-blog.csdnimg.cn/img_convert/5d0e8493f39d5d53de750086c9ea2867.png" alt="image-20210323161913885"></p>
<p>其中“睡觉”或者“吃饭”代表的是状态,而“提供食物”或者“东西移动”则代表了转移。图中这个FSM是对小猫活动的一个抽象（这里并没有刻意写开始状态或者final状态），小猫咪不能同时的即处于“玩耍”又处于“睡觉”状态，并且从一个状态到下一个状态的转换只有一个输入。“睡觉”状态并不知道是从什么状态转换过来的，可能是“玩耍”，也可能是”猫砂窝”。</p>
<p>如果《小猫咪的一天》这个FSM接收以下的输入:</p>
<p>提供食物<br>有大声音<br>安静<br>消化食物<br>那么我们会明确的知道，小猫咪会这样依次变化状态： 睡觉-&gt;吃饭-&gt;躲藏-&gt;吃饭-&gt;猫砂窝.</p>
<p><strong>FST</strong>，不但能共享前缀还能共享后缀。不但能判断查找的key是否存在，还能给出响应的输入output。 它在时间复杂度和空间复杂度上都做了最大程度的优化，使得Lucene能够将Term Dictionary完全加载到内存，快速的定位Term找到响应的output（posting倒排列表）。</p>
<h2 id="Elasticsearch读数据过程"><a href="#Elasticsearch读数据过程" class="headerlink" title="Elasticsearch读数据过程"></a>Elasticsearch读数据过程</h2><p>可以通过 doc id 来查询，会根据 doc id 进行 hash，判断出来当时把 doc id 分配到了哪个 shard 上面去，从那个 shard 去查询。</p>
<ul>
<li>客户端发送请求到任意一个 node，成为 coordinate node 。</li>
<li>coordinate node 对 doc id 进行哈希路由，将请求转发到对应的 node，此时会使用 round-robin <strong>随机轮询</strong><br><strong>算法</strong>，在 primary shard 以及其所有 replica 中随机选择一个，让读请求负载均衡。</li>
<li>接收请求的 node 返回 document 给 coordinate node 。</li>
<li>coordinate node 返回 document 给客户端。</li>
</ul>
<h2 id="Elasticsearch-搜索数据过程"><a href="#Elasticsearch-搜索数据过程" class="headerlink" title="Elasticsearch 搜索数据过程"></a>Elasticsearch 搜索数据过程</h2><p>es 最强大的是做全文检索，就是比如你有三条数据：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">java真好玩儿啊</span><br><span class="line">java好难学啊</span><br><span class="line">j2ee特别牛</span><br></pre></td></tr></table></figure>

<p>你根据 java 关键词来搜索，将包含 java 的 document 给搜索出来。es 就会给你返回：java 真好玩儿啊，java 好难学啊。</p>
<ul>
<li>客户端发送请求到一个 coordinate node 。</li>
<li>协调节点将搜索请求转发到所有的 shard 对应的 primary shard 或 replica shard ，都可以。</li>
<li>query phase：每个 shard 将自己的搜索结果（其实就是一些 doc id ）返回给协调节点，由协调节点进行数据的合并、排序、分页等操作，产出最终结果。</li>
<li>fetch phase：接着由协调节点根据 doc id 去各个节点上拉取实际的 document 数据，最终返回给客户端。</li>
</ul>
<blockquote>
<p>写请求是写入 primary shard，然后同步给所有的 replica shard；读请求可以从 primary shard 或 replica shard 读取，采用的是随机轮询算法。</p>
</blockquote>
<h2 id="写数据底层原理"><a href="#写数据底层原理" class="headerlink" title="写数据底层原理"></a>写数据底层原理</h2><ul>
<li>先写入内存 buffer，在 buffer 里的时候数据是搜索不到的；同时将数据写入 translog 日志文件。</li>
</ul>
<ul>
<li>如果 buffer 快满了，或者到一定时间，就会将内存 buffer 数据 refresh 到一个新的 segment file 中，但是此时数据不是直接进入 segment file 磁盘文件，而是先进入 os cache 。这个过程就是 refresh 。</li>
</ul>
<ul>
<li>每隔 1 秒钟，es 将 buffer 中的数据写入一个新的 segment file ，每秒钟会产生一个新的磁盘文件 segment file ，这个 segment file 中就存储最近 1 秒内 buffer 中写入的数据。</li>
</ul>
<ul>
<li>但是如果 buffer 里面此时没有数据，那当然不会执行 refresh 操作，如果 buffer 里面有数据，默认 1 秒钟执行一次 refresh 操作，刷入一个新的 segment file 中。</li>
</ul>
<ul>
<li>操作系统里面，磁盘文件其实都有一个东西，叫做 os cache ，即操作系统缓存，就是说数据写入磁盘文件之前，会先进入 os cache ，先进入操作系统级别的一个内存缓存中去。只要 buffer 中的数据被 refresh 操作刷入 os cache 中，这个数据就可以被搜索到了。</li>
</ul>
<ul>
<li>为什么叫 es 是准实时的？ NRT ，全称 near real-time 。默认是每隔 1 秒 refresh 一次的，所以 es 是准实时的，因为写入的数据 1 秒之后才能被看到。可以通过 es 的 restful api 或者 java api ，手动执行一次 refresh 操作，就是手动将 buffer 中的数据刷入 os cache 中，让数据立马就可以被搜索到。只要数据被输入 os cache 中，buffer 就会被清空了，因为不需要保留 buffer 了，数据在 translog 里面已经持久化到磁盘去一份了。</li>
</ul>
<ul>
<li>重复上面的步骤，新的数据不断进入 buffer 和 translog，不断将 buffer 数据写入一个又一个新的 segment file 中去，每次 refresh 完 buffer 清空，translog 保留。随着这个过程推进，translog 会变得越来越大。当 translog 达到一定长度的时候，就会触发 commit 操作。</li>
</ul>
<ul>
<li>commit 操作发生第一步，就是将 buffer 中现有数据 refresh 到 os cache 中去，清空 buffer。然后，将一个 commit point 写入磁盘文件，里面标识着这个 commit point 对应的所有 segment file ，同时强行将 os cache 中目前所有的数据都 fsync 到磁盘文件中去。最后清空 现有 translog 日志文件，重启一个 translog，此时 commit 操作完成。</li>
</ul>
<ul>
<li>这个 commit 操作叫做 flush 。默认 30 分钟自动执行一次 flush ，但如果 translog 过大，也会触发 flush 。flush 操作就对应着 commit 的全过程，我们可以通过 es api，手动执行 flush 操作，手动将 os cache 中的数据 fsync 强刷到磁盘上去。</li>
</ul>
<ul>
<li>translog 日志文件的作用是什么？你执行 commit 操作之前，数据要么是停留在 buffer 中，要么是停留在 os cache 中，无论是 buffer 还是 os cache 都是内存，一旦这台机器死了，内存中的数据就全丢了。所以需要将数据对应的操作写入一个专门的日志文件 translog 中，一旦此时机器宕机，再次重启的时候，es 会自动读取 translog 日志文件中的数据，恢复到内存 buffer 和 os cache 中去。</li>
</ul>
<ul>
<li>translog 其实也是先写入 os cache 的，默认每隔 5 秒刷一次到磁盘中去，所以默认情况下，可能有 5 秒的数据会仅仅停留在 buffer 或者 translog 文件的 os cache 中，如果此时机器挂了，会丢失 5 秒钟的数据。但是这样性能比较好，最多丢 5 秒的数据。也可以将 translog 设置成每次写操作必须是直接 fsync 到磁盘，但是性能会差很多。</li>
</ul>
<p>实际上你在这里，如果面试官没有问你 es 丢数据的问题，你可以在这里给面试官炫一把，你说，其实 es 第一是准实时的，数据写入 1 秒后可以搜索到；可能会丢失数据的。有 5 秒的数据，停留在 buffer、translog os cache、segment file os cache 中，而不在磁盘上，此时如果宕机，会导致 5 秒的数据丢失。</p>
<p>总结一下，数据先写入内存 buffer，然后每隔 1s，将数据 refresh 到 os cache，到了 os cache 数据就能被搜索到（所以我们才说 es 从写入到能被搜索到，中间有 1s 的延迟）。每隔 5s，将数据写入 translog 文件（这样如果机器宕机，内存数据全没，最多会有 5s 的数据丢失），translog 大到一定程度，或者默认每隔 30mins，会触发 commit 操作，将缓冲区的数据都 flush 到 segment file 磁盘文件中。</p>
<h2 id="Elasticsearch-在部署时，对-Linux-的设置有哪些优化方法？"><a href="#Elasticsearch-在部署时，对-Linux-的设置有哪些优化方法？" class="headerlink" title="Elasticsearch 在部署时，对 Linux 的设置有哪些优化方法？"></a>Elasticsearch 在部署时，对 Linux 的设置有哪些优化方法？</h2><ul>
<li><p>64 GB 内存的机器是非常理想的， 但是 32 GB 和 16 GB 机器也是很常见的。少于 8 GB 会适得其反。</p>
</li>
<li><p>如果你要在更快的 CPUs 和更多的核心之间选择，选择更多的核心更好。多个内核提供的额外并发远胜过稍微快一点点的时钟频率。</p>
</li>
<li><p>如果你负担得起 SSD，它将远远超出任何旋转介质。 基于 SSD 的节点，查询和索引性能都有提升。如果你负担得起，SSD 是一个好的选择。</p>
</li>
<li><p> 即使数据中心们近在咫尺，也要避免集群跨越多个数据中心。绝对要避免集群跨越大的地理距离。</p>
</li>
</ul>
<ul>
<li>请确保运行你应用程序的 JVM 和服务器的 JVM 是完全一样的。 在Elasticsearch 的几个地方，使用 Java 的本地序列化。</li>
</ul>
<ul>
<li>通过设置 gateway.recover_after_nodes、gateway.expected_nodes、gateway.recover_after_time 可以在集群重启的时候避免过多的分片交换，这可能会让数据恢复从数个小时缩短为几秒钟。</li>
</ul>
<ul>
<li>Elasticsearch 默认被配置为使用单播发现，以防止节点无意中加入集群。只有在同一台机器上运行的节点才会自动组成集群。最好使用单播代替组播。</li>
</ul>
<ul>
<li>不要随意修改垃圾回收器（CMS）和各个线程池的大小。</li>
</ul>
<ul>
<li>把你的内存的（少于）一半给 Lucene（但不要超过 32 GB！），通过ES_HEAP_SIZE 环境变量设置。</li>
</ul>
<ul>
<li>内存交换到磁盘对服务器性能来说是致命的。如果内存交换到磁盘上，一个100 微秒的操作可能变成 10 毫秒。 再想想那么多 10 微秒的操作时延累加起来。 不难看出 swapping 对于性能是多么可怕。</li>
</ul>
<ul>
<li>Lucene 使用了大量 的文件。同时，Elasticsearch 在节点和 HTTP 客户端之间进行通信也使用了大量的套接字。 所有这一切都需要足够的文件描述符。你应该增加你的文件描述符，设置一个很大的值，如 64,000。</li>
</ul>
<h2 id="在并发情况下，Elasticsearch-如果保证读写一致"><a href="#在并发情况下，Elasticsearch-如果保证读写一致" class="headerlink" title="在并发情况下，Elasticsearch 如果保证读写一致"></a>在并发情况下，Elasticsearch 如果保证读写一致</h2><ul>
<li>可以通过版本号使用乐观并发控制，以确保新版本不会被旧版本覆盖，由应用层来处理具体的冲突</li>
</ul>
<ul>
<li><p>另外对于写操作，一致性级别支持 quorum/one/all，默认为 quorum，即只有当大多数分片可用时才允许写操作。但即使大多数可用，也可能存在因为网络等原因导致写入副本失败，这样该副本被认为故障，分片将会在一个不同的节点上重建。</p>
</li>
<li><p>对于读操作，可以设置 replication 为 sync(默认)，这使得操作在主分片和副本分片都完成后才会返回；如果设置 replication 为 async 时，也可以通过设置搜索请求参数_preference 为 primary 来查询主分片，确保文档是最新版本。</p>
</li>
</ul>
<h3 id="从动态索引上优化"><a href="#从动态索引上优化" class="headerlink" title="从动态索引上优化"></a>从动态索引上优化</h3><p>基于模板+时间+rollover api 滚动创建索引，举例：设计阶段定义：blog 索</p>
<p>引的模板格式为：blog_index_时间戳的形式，每天递增数据。</p>
<p>这样做的好处：不至于数据量激增导致单个索引数据量非常大，接近于上线 2 的</p>
<p>32 次幂-1，索引存储达到了 TB+甚至更大。</p>
<p>一旦单个索引很大，存储等各种风险也随之而来，所以要提前考虑+及早避免。</p>
<h3 id="从存储层面优化"><a href="#从存储层面优化" class="headerlink" title="从存储层面优化"></a>从存储层面优化</h3><p>冷热数据分离存储，热数据（比如最近 3 天或者一周的数据），其余为冷数据。</p>
<p>对于冷数据不会再写入新数据，可以考虑定期 force_merge 加 shrink 压缩操作，</p>
<p>节省存储空间和检索效率。</p>
<h3 id="从部署层面优化"><a href="#从部署层面优化" class="headerlink" title="从部署层面优化"></a>从部署层面优化</h3><p>一旦之前没有规划，这里就属于应急策略。</p>
<p>结合 ES 自身的支持动态扩展的特点，动态新增机器的方式可以缓解集群压力，注</p>
<p>意：如果之前主节点等规划合理，不需要重启集群也能完成动态新增的。</p>
<h2 id="Elasticsearch-集群配置"><a href="#Elasticsearch-集群配置" class="headerlink" title="Elasticsearch 集群配置"></a>Elasticsearch 集群配置</h2><p>es 生产集群我们部署了 5 台机器，每台机器是 6 核 64G 的，集群总内存是 320G。<br>我们 es 集群的日增量数据大概是 2000 万条，每天日增量数据大概是 500MB，每月增量数据大概是 6 亿，15G。目前系统已经运行了几个月，现在 es 集群里数据总量大概是 100G 左右。<br>目前线上有 5 个索引（这个结合你们自己业务来，看看自己有哪些数据可以放 es 的），每个索引的数据量大概是 20G，所以这个数据量之内，我们每个索引分配的是 8 个 shard，比默认的 5 个 shard 多了 3 个 shard。</p>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="https://tomstand.gitee.io/2023/11/21/%E5%88%86%E5%B8%83%E5%BC%8F%E6%B6%88%E6%81%AF%E5%A4%84%E7%90%86%E6%9C%BA%E5%88%B6-kafka/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="tomstand">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="猿日记">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | 猿日记">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2023/11/21/%E5%88%86%E5%B8%83%E5%BC%8F%E6%B6%88%E6%81%AF%E5%A4%84%E7%90%86%E6%9C%BA%E5%88%B6-kafka/" class="post-title-link" itemprop="url">Untitled</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">Posted on</span>

      <time title="Created: 2023-11-21 20:16:19" itemprop="dateCreated datePublished" datetime="2023-11-21T20:16:19+08:00">2023-11-21</time>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <p>#安装kafka</p>
<ol>
<li>tar -zxvf</li>
<li>进入到config目录下修改server.properties<br>broker.id<br>listeners=PLAINTEXT://192.168.11.140:9092<br>zookeeper.connect</li>
<li>启动<br>sh  kafka-server-start.sh   -daemon  ../config/server.properties<br>sh  kafka-server-stop.sh</li>
</ol>
<p>zookeeper上注册的节点信息<br>cluster, controller, controller_epoch, brokers, zookeeper, admin, isr_change_notification, consumers, latest_producer_id_block, config</p>
<p>controller – 控制节点<br>brokers  – kafka集群的broker信息 。 topic<br>consumer  ids/owners/offsets</p>
<p>#基本操作<br><img src="https://img-blog.csdnimg.cn/img_convert/8ec3de65786b72f480c33d2dcbbdf01a.png" alt="image.png"></p>
<p><img src="https://img-blog.csdnimg.cn/img_convert/fc1dd1bb2db553788ee79e4be26c434c.png" alt="image.png"></p>
<p><img src="https://img-blog.csdnimg.cn/img_convert/b4ba606f6b61e9d2aac2523f41b887ed.png" alt="image.png"></p>
<p><a target="_blank" rel="noopener" href="http://kafka.apache.org/documentation/#quickstart">http://kafka.apache.org/documentation/#quickstart</a></p>
<p>#kafka的实现细节<br>消息是kafka中最基本的数据单元，消息由一串字节构成，其中主要由key和value构成，key和value都是byte数组。key的主要作用是根据一定的策略，将消息路由到指定的分区，这样就可以保证包含同一key的消息全部写入到同一个分区内，key可以是null。为了提高网络的存储和利用率，生产者会批量发送消息到kafka，并在发送之前消息进行压缩</p>
<p>#topic&amp;partitiion</p>
<ul>
<li>Topic是用于存储消息的逻辑概念，可以看做一个消息集合。每个topic可以有多个生产者向其推送消息，也可以有任意多个消费者消费其中的消息</li>
<li>每个topic可以划分多个分区（每个Topic至少有一个分区），同一topic下的不同分区包含的消息是不同的。每个消息在被添加到分区时，都会被分配一个offset（称之为偏移量），他是消息在此分区中的唯一编号，kafka通过offset保证消息在分区内的顺序，offset的顺序不跨分区，即kafka只保证在同一分区内的消息是有序的；</li>
</ul>
<p><img src="https://img-blog.csdnimg.cn/img_convert/be462ab5f0580ed1121a84e78ded0847.png" alt="image.png"></p>
<p><img src="https://img-blog.csdnimg.cn/img_convert/d708adf816cfca0d0a765ac16ce6d5cd.png" alt="image.png"></p>
<p><img src="https://img-blog.csdnimg.cn/img_convert/2931882f21896c5185cf564696e6fabd.png" alt="image.png"></p>
<p>Partition是以文件的形式存储在文件系统中，存储在kafka-log目录下，命名规则是：<topic_name>-<partition_id></p>
<p>#kafka的高吞吐量的因素</p>
<ol>
<li>顺序写的方式存储数据；</li>
<li>批量发送；在异步发送模式中，kafka允许进行批量发送，也就是先讲消息缓存到内存中，然后一次请求批量发送出去。这样减少了磁盘频繁IO以及网络IO造成的性能瓶颈。<br>batch.size 每批次发送的数据大小<br>linger.ms    间隔时间</li>
<li>零拷贝<br>消息从发送到落地保存，broker维护的消息日志本身就是文件目录，每个文件都是二进制保存，生产者和消费者使用相同的格式来处理。在消费者获取消息时，服务器先从硬盘读取数据到内存，然后把内存中的数据原封不动的通过socket发送给消费者，虽然这个操作描述起来很简单，但实际上经历了很多步骤</li>
</ol>
<ul>
<li>操作系统将数据从磁盘读入到内核空间的页缓存</li>
<li>应用程序将数据从内核空间读入到用户空间缓存中</li>
<li>应用程序将数据写回到内核空间到socket缓存中</li>
<li>操作系统将数据从socket缓冲区复制到网卡缓冲区，以便将数据经网络发出<br><img src="https://img-blog.csdnimg.cn/img_convert/eaf96117f049b19f856ae2ecb873e2b8.png" alt="image.png"></li>
</ul>
<p> 通过“零拷贝”技术可以去掉这些没必要的数据复制操作，同时也会减少上下文切换次数<br><img src="https://img-blog.csdnimg.cn/img_convert/77e65a2ab25dced9413bcd3b1a089a8a.png" alt="image.png"></p>
<p>#日志策略<br>###日志保留策略</p>
<ul>
<li>无论消费者是否已经消费了消息，kafka都会一直保存这些消息，但并不会像数据库那样长期保存，为了避免磁盘被占满，kafka会配置相应的保留策略（retention policy），以实现周期性地删除陈旧的消息</li>
</ul>
<p><strong>kafka有两种“保留策略”：</strong></p>
<ol>
<li>根据消息保留的时间，当消息在kafka中保存的时间超过了指定时间，就可以被删除；</li>
<li>根据topic存储的数据大小，当topic所占的日志文件大小大于一个阈值，则可以开始删除最旧的消息</li>
</ol>
<p>###日志压缩策略<br>在很多场景中，消息的key与value的值之间的对应关系是不断变化的，就像数据库中的数据会不断被修改一样，消费者只关心key对应的最新的value。我们可以开启日志压缩功能，kafka定期将相同key的消息进行合并，只保留最新的value值<br><img src="https://img-blog.csdnimg.cn/img_convert/2e867fbaab79ca7b34b77035bf759097.png" alt="image.png"></p>
<p>#消息可靠性机制<br><strong>消息可靠性</strong><br>生产者发送消息到broker，有三种确认方式（request.required.acks）</p>
<ul>
<li>acks = 0：producer不会等待broker（leader）发送ack。因为发送消息网络超时或broker crash（1.Partition的Leader还没有commit消息 2. Leader与Follower数据不同步），既有可能丢失也可能会重发。</li>
<li>acks = 1：当leader接收到消息之后发送ack，丢会重发，丢的概率很小。</li>
<li>acks = -1：当所有的follower都同步消息成功后发送ack，丢失消息可能性比较低。</li>
</ul>
<p><strong>消息存储可靠性</strong></p>
<ul>
<li>每一条消息被发送到 broker 中，会根据 partition 规则选择被存储到哪一个partition。如果 partition 规则设置的合理，所有消息可以均匀分布到不同的 partition 里，这样就实现了水平扩展。</li>
<li>在创建topic时可以指定这个topic对应的 partition 的数据。在发送一条消息时，可以指定这条消息的key， producer 根据这个 key 和 partition机制来判断这个消息发送到哪个 partition。</li>
<li>kafka的高可靠性的保障来自于另一个叫副本（replication）策略，通过设置副本的相关参数，可以使kafka在性能和可靠性之间做不同的切换。</li>
</ul>
<p><strong>高可靠性的副本</strong></p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">sh kafka-topics.sh --create --zookeeper 192.168.11.140:2181 </span><br><span class="line">--replication-factor 2  --partitions 3 --topic sixsix </span><br></pre></td></tr></table></figure>
<ul>
<li>–replication-factor 表示的副本数</li>
</ul>
<p><strong>副本机制</strong><br>ISR（副本同步队列）<br>维护的是有资格的 follower 节点</p>
<ol>
<li>副本的所有节点都必须要和 zookeeper 保持连接状态</li>
<li>副本的最后一条消息的 offset 和 leader 副本的最后一条消息的 offset 之间的差值不能超过指定的阈值，这个阈值是可以设置的（replica.lag.max.messages）</li>
</ol>
<p><strong>HW&amp;LEO</strong><br>关于 follower 副本同步的过程中，还有两个关键的概念，<strong>HW（HighWatermark）和 LEO（Log End Offset）</strong> 这两个参数跟 ISR 集合紧密关联。</p>
<ul>
<li>HW标记了一个特殊的 offset，当消费者处理消息的时候，只能拉取到 HW 之前的消息，HW之后的消息对消费者来说是不可见的。也就是说，取 partition 对应 ISR中最小的 LEO 作为 HW，consumer 最多只能消费到 HW所在的位置。每个 replica 都有 HW，leader 和 follower 各自维护更新自己的 HW 的状态。对于leader新写入的消息，consumer不能立刻消费，leader 会等待该消息被所有ISR中的 replicas 同步更新 HW ，此时消息才能被 consumer 消费。 这样就保证了如果leader副本损坏，该消息仍然可以从新选举的leader 中获取</li>
<li>LEO是所有副本都会有的一个offset 标记，它指向追加到当前副本的最后一个消息的 offset，当生产者向 leader 副本追加消息的时候， leader 副本的LEO 标记就会递增； 当follower 副本成功从leader副本拉取消息并更新到本地的时候，follower的副本的LEO就会增加</li>
</ul>
<p>#查看kafka数据文件内容<br>在使用kafka的过程中有时候需要我们查看产生的消息的信息，这些都被记录在kafka的log文件中。由于log 文件的特殊格式，需要通过kafka提供的工具来查看</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">./bin/kafka-run-class.sh kafka.tools.DumpLogSegments </span><br><span class="line">--files /tmp/kafka-logs*/000**.log  --print-data-log&#123;查看消息内容&#125;</span><br></pre></td></tr></table></figure>

<p>#文件存储机制<br><strong>存储机制</strong><br>在kafka文件存储中，同一个topic下有多个不同的partition，每个partition为一个目录，partition的名称规则为：topic名称 + 有序序号，第一个序号从0开始，最大的序号为partition数量减1，partition是实际上物理上的概念，而topic是逻辑上的概念<br><strong>partition还可以细分为segment</strong>，这个segment是什么呢？假设kafka以partition为最小存储单位，那么我们可以想象当kafka的producer不断发送消息，必然会引起partition文件的无限扩张，这样对于消息文件的维护以及被消费的消息的清理带来非常大的挑战，所以kafka以segment为单位又把partition进行细分。每个partition相当于一个巨型文件被平均分配到多个大小相等的segment数据文件中（每个segment文件中的消息不一定相等）。这种特性方便已经被消费的清理，提高磁盘的利用率<br><strong>segment file组成</strong>：由2大部分组成，分别为index file 和data file，此2个文件一一对应，成对出现，后缀“.index”和“.log”分别表示segment索引文件、数据文件。<br><strong>segment文件命名规则</strong>：partition全局的第一个segment从0开始，后续每个segment文件名为上一个segment文件最后一条消息的offset。数值最大为64位long大小，19位数字字符长度，没有数字用0填充</p>
<p><img src="https://img-blog.csdnimg.cn/img_convert/f8daf8d7d736bb15a3c94928d43d79f8.png" alt="image.png"></p>
<p><img src="https://img-blog.csdnimg.cn/img_convert/6b9a9be1f1fd73f499dd53045e780234.png" alt="image.png"></p>
<p>#查找方式<br>以上图为例，读取offset=170218的消息，首先查找segment文件，其中00000000000000000000.index为最开始的文件，第二个文件为00000000000000170410.index（起始偏移为170410+1=170411），而第三个文件为00000000000000239430.index（起始偏移为239430+1=239431），所以这个offset=170418就落到了第二个文件之中。其他后续文件可以依次类推，以其实偏移量命名并排列这些文件，然后根据二分查找法就可以快速定位到具体文件位置，其次根据00000000000000170410.index文件中的[8,1325]定位到00000000000000170410.log文件中的1325的位置进行读取。</p>
<p>#消息确认的几种方式<br><strong>自动提交</strong><br><img src="https://img-blog.csdnimg.cn/img_convert/5f929623c2fd9bc43ef964d8d86d7f08.png" alt="image.png"></p>
<p><strong>手动提交</strong><br>手动异步提交<br>consumer. commitASync() //手动异步ack<br>手动同步提交<br>consumer. commitSync() //手动异步ack</p>
<p>#指定消费某个分区的消息<br><img src="https://img-blog.csdnimg.cn/img_convert/db0dad00d57de5dade011d2ec2ba1ba9.png" alt="image.png"></p>
<p>#消息的消费原理<br>之前kafka存在的一个非常大的性能隐患就是利用ZK来记录各个Consumer  Group的消费进度（offset）。当然JVM Client 帮我们自动做了这些事情，但是Consumer需要和ZK频繁交互，则利用ZK Client API对ZK频繁写入是一个低效的操作，并且从水平扩展性上来讲也存在问题，所以ZK抖一抖，集群吞吐量就跟着一起抖，严重的时候简直抖的停不下来。<br>新版的kafka已推荐将consumer的文艺信息保存在Kfaka内部的topic,即_consumer_offsets_topic.通过以下操作来看看_consumer_offsets_topic是怎么存储消费进度的，_consumer_offset_topic 默认有50个分区</p>
<ol>
<li>计算 consumer  group对应的hash值<br><img src="https://img-blog.csdnimg.cn/img_convert/02f3077be0c933f1d988cde662a77317.png" alt="image.png"></li>
</ol>
<p>2.获得 consumer group 的位移信息</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">bin/kafka-simple-consumer-shell.sh </span><br><span class="line">--topic __consumer_offsets --partition 15 </span><br><span class="line">-broker-list 192.168.11.140:9092,192.168.11.141:9092,192.168.11.138:9092   </span><br><span class="line">--formatter kafka.coordinator.group.GroupMetadataManager\$OffsetsMessageFormatter</span><br></pre></td></tr></table></figure>

<p>#kafka的分区分配策略<br>在kafka中每个topic一般会有很多个partitions。为了提高消息的消费速度，我们可能会启动多个consumer去消费；同时，kafka存在consumer group的概念，也就是group.id一样的consumer，这些consumer属于一个consumer group，组内的所有消费者协调在一起来消费订阅主题的所有分区，当然每一个分区只能由同一个消费组内的consumer来消费，那么同一个consumer group里面的consumer是怎么去分配该消费那个分区里的数据，这个就涉及到了kafka内部<strong>分区分配策略（Partition Assignment Strategy）</strong><br><strong>在 Kafka内部存在两种默认的分区分配策略</strong>：Range（默认）和 RoundRobin。通过：partition.assignment.strategy指定<br>#consumer rebalance<br>当以下事件发生时，Kafka将会进行一次分区分配：</p>
<ol>
<li>同一个consumer group内新增了消费者</li>
<li>消费者离开当前所属的consumer  group，包括shuts down 或 crashes</li>
<li>订阅的主题新增分区（分区数量发生变化）</li>
<li>消费者主动取消对某个topic的订阅</li>
<li>也就是说，把分区的所有权从一个消费者移动到另外一个消费者上，这个是kafka  consumer 的 rebalance机制，如何 rebalance就涉及到前面说的分区分配策略</li>
</ol>
<p>两种分区策略<br>Range 策略（默认）<br>0 ，1 ，2 ，3 ，4，5，6，7，8，9<br>c0 [0,3] c1 [4,6] c2 [7,9]<br>10(partition num/3(consumer num) =3<br>roundrobin 策略<br>0 ，1 ，2 ，3 ，4，5，6，7，8，9<br>c0,c1,c2<br>c0 [0,3,6,9]<br>c1 [1,4,7]<br>c2 [2,5,8]<br>kafka 的key 为null， 是随机｛一个Metadata的同步周期内，默认是10分钟｝</p>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="https://tomstand.gitee.io/2023/11/21/%E5%88%86%E5%B8%83%E5%BC%8F%E5%8D%8F%E8%B0%83%E6%9C%8D%E5%8A%A1-Zookeeper/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="tomstand">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="猿日记">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | 猿日记">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2023/11/21/%E5%88%86%E5%B8%83%E5%BC%8F%E5%8D%8F%E8%B0%83%E6%9C%8D%E5%8A%A1-Zookeeper/" class="post-title-link" itemprop="url">Untitled</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">Posted on</span>

      <time title="Created: 2023-11-21 20:16:19" itemprop="dateCreated datePublished" datetime="2023-11-21T20:16:19+08:00">2023-11-21</time>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="分布式环境的特点"><a href="#分布式环境的特点" class="headerlink" title="分布式环境的特点"></a>分布式环境的特点</h1><ul>
<li>分布性</li>
<li>并发性<br>程序运行过程中，并发性操作室很常见的。比如同一个分布式系统中的多个节点，同时访问一个共享资源。数据库、分布式存储</li>
<li>无序性<br>进程之间的消息通信，会出现顺序不一致问题<br>#分布式环境下面临的问题</li>
<li>网络通信<br>网络本身的不可靠性，因此会涉及到一些网络通信的问题</li>
<li>网络分区（脑裂）<br>当网络发生异常倒是分布式系统中部分节点之间的网络延时不断增大，最终导致组成分布式架构的所有节点，只有部分节点能正常通信</li>
<li>三态<br>在分布式架构里面，有三种状态，成功、失败、超时</li>
<li>分布式事务<br>ACID（原子性、一致性、隔离性、持久性）<br>#中心化和去中心化</li>
<li>冷备或者热备<br>分布式架构里，很多的架构思想采用的是：当集群发生故障的时候，集群中的人群会自动“选举”出一个新的领导。<br>最典型的是：zookeeper/etcd<br>#经典的CAP/BASE理论<br>##CAP<br>C（一致性Consistency）：所有节点上的数据，时刻保持一致<br>A（可用性Availability）：每个请求都能够收到一个响应，无论响应成功或者失败<br>P（分区容错Partition-tolerance）：表示系统出现脑裂以后，可能导致某些server与集群中的其他机器失去联系<br>CP/AP<br>CAP理论仅适用于原子读写的Nosql场景，不适用于数据库系统<br>##BASE</li>
<li>基于CAP理论，CAP理论并不适用于数据库事务（因为更新一些错误的数据而导致数据出现紊乱，无论什么样的数据库高可用方案都是徒劳），虽然XA事务可以保证数据库在分布式系统下的ACID特性，但是会带来性能方面的影响；</li>
<li>eBay尝试了一种完全不同的套路，放宽了对事务ACID的要求。提出了BASE理论   <ul>
<li>Basically available：数据库采用分片模式，把100W的用户数据分布在5个实例上。如果破坏了其中一个实例，仍然可以保证80%的用户可用</li>
<li>soft-state：在基于client-server模式的系统中，server端是否有状态，决定了系统是否具备良好的水平扩展、负载均衡、故障恢复等特性。Server端承诺会维护client端状态数据，这个状态仅仅维护一小段时间，这段时间以后，server端就会丢弃这个状态，恢复正常状态</li>
<li>Eventually consistent ：数据的最终一致性</li>
</ul>
</li>
</ul>
<h1 id="初步认识zookeeper"><a href="#初步认识zookeeper" class="headerlink" title="初步认识zookeeper"></a>初步认识zookeeper</h1><ul>
<li>zookeeper是一个开源的分布式协调服务，是由雅虎创建的，基于google chubby</li>
<li>zookeeper是什么<br>分布式数据一致性的解决方案</li>
<li>zookeeper能做什么<br>数据的发布/订阅（配置中心：disconf）、负载均衡（dubbo利用了zookeeper机制实现负载均衡）、命名服务、master选举（kafka、hadoop、hbase）、分布式队列、分布式锁</li>
<li>zookeeper的特性<ul>
<li>顺序一致性<br>从同一个客户端发起的事务请求，最终会严格按照顺序被应用到zookeeper中</li>
<li>原子性<br>所有的事务请求的处理结果在整个集群中的所有机器上的应用情况是一致的，也就是说，要么整个集群中的所有机器都成功的应用了某一事务，要么全都不应用</li>
<li>可靠性<br>一旦服务器成功应用了某一个事务数据，并且对客户端做了相应，那么这个数据在整个集群中一定是同步并且保留下来的</li>
<li>实时性<br>一旦一个事务被成功应用，客户端就能够立即从服务器端读取到事务变更后的最新数据状态；（zookeeper仅仅保证在一定时间内，近实时）</li>
</ul>
</li>
</ul>
<h1 id="zookeeper安装"><a href="#zookeeper安装" class="headerlink" title="zookeeper安装"></a>zookeeper安装</h1><ul>
<li>单机环境安装</li>
</ul>
<ol>
<li>下载zookeeper的安装包<br><a target="_blank" rel="noopener" href="http://apache.fayea.com/zookeeper/stable/zookeeper-3.4.10.tar.gz">http://apache.fayea.com/zookeeper/stable/zookeeper-3.4.10.tar.gz</a></li>
<li>解压zookeeper<br>tar -zxvf zookeeper-3.4.10.tar.gz</li>
<li>cd 到 ZK_HOME/conf  , copy一份zoo.cfg<br>cp  zoo_sample.cfg  zoo.cfg</li>
<li>sh zkServer.sh<br>{start|start-foreground|stop|restart|status|upgrade|print-cmd}</li>
<li>sh zkCli.sh -server  ip:port</li>
</ol>
<ul>
<li>集群环境<br>zookeeper集群, 包含三种角色: leader / follower /observer<br>observer<br>observer是一种特殊的zookeeper节点。可以帮助zookeeper的扩展性（如果大量客户端访问我们zookeeper集群需要增加zookeeper集群机器数量。从而增加zookeeper集群的性能，导致zookeeper写性能下降，zookeeper的数据变更需要半数以上服务器投票通过，造成网络消耗增加投票成本）</li>
</ul>
<ol>
<li>observer不参与投票，只接受投票结果</li>
<li>不属于zookeeper的关键部位</li>
</ol>
<p><img src="https://img-blog.csdnimg.cn/img_convert/2263320362d31ff6cee7abc6f246b79a.png" alt="image.png"></p>
<ol>
<li>在zoo.cfg里面增加<br>peerType=observer<br>server.1=192.168.11.129:2181:3181:observer<br>server.2=192.168.11.131:2181:3181<br>server.3=192.168.11.135:2181:3181</li>
</ol>
<p>第一步： 修改配置文件<br>server.id=host:port:port<br>id的取值范围： 1~255； 用id来标识该机器在集群中的机器序号<br>2181是zookeeper的端口； //3306<br>3181表示leader选举的端口<br>server.1=192.168.11.129:2181:3181<br>server.2=192.168.11.131:2181:3181<br>server.3=192.168.11.135:2181:3181</p>
<p>第二步：创建myid<br>在每一个服务器的dataDir目录下创建一个myid的文件，文件就一行数据，数据内容是每台机器对应的server ID的数字</p>
<p>第三步：启动zookeeper</p>
<p>192.168.11.129<br>192.168.11.131<br>192.168.11.135</p>
<h2 id="zoo-cfg配置文件分析"><a href="#zoo-cfg配置文件分析" class="headerlink" title="zoo.cfg配置文件分析"></a>zoo.cfg配置文件分析</h2><ul>
<li>tickTime=2000<br>zookeeper中最小的时间单位长度（ms）</li>
<li>initLimit=10<br>follower节点启动后与leader节点完成数据同步的时间</li>
<li>syncLimit=5<br>leader节点与follower节点进行心跳监测的最大延时时间</li>
<li>dataDir=/tmp/zookeeper<br>表示zookeeper服务器存储快照文件的目录</li>
<li>dataLogDir 表示配置zookeeper事务日志的存储路径，默认指定在dataDir目录下</li>
<li>clientPort  表示客户端和服务端建立连接的端口号： 2181</li>
</ul>
<h1 id="zookeeper中的一些概念"><a href="#zookeeper中的一些概念" class="headerlink" title="zookeeper中的一些概念"></a>zookeeper中的一些概念</h1><h3 id="数据模型"><a href="#数据模型" class="headerlink" title="数据模型"></a>数据模型</h3><p>zookeeper的数据模型和文件系统类似，每一个节点称为：znode 是zookeeper中的最小数据单元，每一个znode上都可以保存数据和挂载子节点，从而构成一个层次化的属性结构</p>
<h3 id="节点特性"><a href="#节点特性" class="headerlink" title="节点特性"></a>节点特性</h3><ul>
<li>持久化节点：节点创建后会一直存在zookeeper服务器上，直到主动删除</li>
<li>持久化有序节点：每个节点都会为它的一级子节点维护一个顺序</li>
<li>临时节点：临时节点的生命周期和客户端的回话保持一致。当客户端回话失效，该节点自动清理</li>
<li>临时有序节点：在临时节点上多了一个顺序性特性</li>
</ul>
<h3 id="会话"><a href="#会话" class="headerlink" title="会话"></a>会话</h3><p><img src="https://img-blog.csdnimg.cn/img_convert/8af3acb063c51fac6b0f0b93dd1f7635.png" alt="image.png"></p>
<ul>
<li>watcher<ul>
<li>zookeeper提供了分布式数据发布/订阅，zookeeper允许客户端向服务器注册一个watcher监听，当服务器的节点触发指定事件的时候会触发watcher服务器会向客户端发送一个事件通知，<strong>watcher的通知是一次性，一旦触发一次通知后，该watcher就失效</strong></li>
</ul>
</li>
<li>ACL<ul>
<li>zookeeper提供控制及诶单访问权限的功能，用于有效的保证zookeeper中数据的安全性，避免误操作而导致系统出现重大事故<br>CREATE /READ/WRITE/DELETE/ADMIN</li>
</ul>
</li>
</ul>
<h3 id="zookeeper的命令操作"><a href="#zookeeper的命令操作" class="headerlink" title="zookeeper的命令操作"></a>zookeeper的命令操作</h3><ol>
<li>create [-s] [-e] path data acl</li>
</ol>
<p>-s 表示节点是否有序<br>-e 表示是否为临时节点<br>默认情况下，是持久化节点<br>2.  get path [watch]<br>获得指定 path的信息</p>
<ol start="3">
<li>set path data [version]<br>修改节点 path对应的data<br>乐观锁的概念<br>数据库里面有一个 version 字段去控制数据行的版本号</li>
<li>delete path [version]<br>删除节点<br>stat信息<br>cversion = 0       子节点的版本号<br>aclVersion = 0     表示acl的版本号，修改节点权限<br>dataVersion = 1    表示的是当前节点数据的版本号<br>czxid    节点被创建时的事务ID<br>mzxid   节点最后一次被更新的事务ID<br>pzxid    当前节点下的子节点最后一次被修改时的事务ID<br>ctime = Sat Aug 05 20:48:26 CST 2017<br>mtime = Sat Aug 05 20:48:50 CST 2017<br>cZxid = 0x500000015<br>ctime = Sat Aug 05 20:48:26 CST 2017<br>mZxid = 0x500000016<br>mtime = Sat Aug 05 20:48:50 CST 2017<br>pZxid = 0x500000015<br>cversion = 0<br>dataVersion = 1<br>aclVersion = 0<br>ephemeralOwner = 0x0   创建临时节点的时候，会有一个sessionId 。 该值存储的就是这个sessionid<br>dataLength = 3    数据值长度<br>numChildren = 0  子节点数</li>
</ol>
<h3 id="java-api的使用"><a href="#java-api的使用" class="headerlink" title="java api的使用"></a>java api的使用</h3><ul>
<li><p>权限控制模式</p>
<ul>
<li>schema：授权对象</li>
<li>ip     : 192.168.1.1</li>
<li>Digest  : username:password</li>
<li>world  : 开放式的权限控制模式，数据节点的访问权限对所有用户开放。 world:anyone</li>
<li>super  ：超级用户，可以对zookeeper上的数据节点进行操作</li>
</ul>
</li>
<li><p>连接状态</p>
<ul>
<li>KeeperStat.Expired  在一定时间内客户端没有收到服务器的通知， 则认为当前的会话已经过期了。</li>
<li>KeeperStat.Disconnected  断开连接的状态</li>
<li>KeeperStat.SyncConnected  客户端和服务器端在某一个节点上建立连接，并且完成一次version、zxid同步</li>
<li>KeeperStat.authFailed  授权失败</li>
</ul>
</li>
<li><p>事件类型</p>
<ul>
<li>NodeCreated  当节点被创建的时候，触发</li>
<li>NodeChildrenChanged  表示子节点被创建、被删除、子节点数据发生变化</li>
<li>NodeDataChanged    节点数据发生变化</li>
<li>NodeDeleted        节点被删除</li>
<li>None   客户端和服务器端连接状态发生变化的时候，事件类型就是None</li>
</ul>
</li>
</ul>
<h3 id="zkclient"><a href="#zkclient" class="headerlink" title="zkclient"></a>zkclient</h3><p><img src="https://img-blog.csdnimg.cn/img_convert/95111e59247f62799d1d349a73375db5.png" alt="image.png"></p>
<h3 id="curator"><a href="#curator" class="headerlink" title="curator"></a>curator</h3><p>Curator本身是Netflix公司开源的zookeeper客户端；</p>
<ul>
<li>curator提供了各种应用场景的实现封装</li>
<li>curator-framework  提供了fluent风格api</li>
<li>curator-replice     提供了实现封装</li>
</ul>
<p>curator连接的重试策略</p>
<ul>
<li>ExponentialBackoffRetry()  衰减重试 </li>
<li>RetryNTimes 指定最大重试次数</li>
<li>RetryOneTime 仅重试一次</li>
<li>RetryUnitilElapsed 一直重试知道规定的时间</li>
</ul>
<h3 id="zookeeper的实际应用场景"><a href="#zookeeper的实际应用场景" class="headerlink" title="zookeeper的实际应用场景"></a>zookeeper的实际应用场景</h3><p>zookeeper能够实现哪些场景<br>订阅发布<br>   watcher机制<br>   统一配置管理（disconf）<br>分布式锁<br>   redis<br>   zookeeper<br>   数据库   </p>
<p>负载均衡<br>ID生成器<br>分布式队列<br>统一命名服务<br>master选举</p>
<p>分布式锁</p>
<p>master选举</p>
<h3 id="数据发布订阅-配置中心"><a href="#数据发布订阅-配置中心" class="headerlink" title="数据发布订阅/ 配置中心"></a>数据发布订阅/ 配置中心</h3><p>实现配置信息的集中式管理和数据的动态更新</p>
<p>实现配置中心有两种模式：push 、pull。<br>长轮训<br>zookeeper采用的是推拉相结合的方式。 客户端向服务器端注册自己需要关注的节点。一旦节点数据发生变化，那么服务器端就会向客户端<br>发送watcher事件通知。客户端收到通知后，主动到服务器端获取更新后的数据</p>
<ol>
<li>数据量比较小</li>
<li>数据内容在运行时会发生动态变更</li>
<li>集群中的各个机器共享配置</li>
</ol>
<h3 id="负载均衡"><a href="#负载均衡" class="headerlink" title="负载均衡"></a>负载均衡</h3><p>请求/数据分摊多个计算机单元上</p>
<h3 id="分布式锁"><a href="#分布式锁" class="headerlink" title="分布式锁"></a>分布式锁</h3><p>通常实现分布式锁有几种方式</p>
<ol>
<li>redis。 setNX 存在则会返回0， 不存在</li>
<li>数据方式去实现<br>创建一个表， 通过索引唯一的方式<br>create table (id , methodname …)   methodname增加唯一索引<br>insert 一条数据XXXdelete 语句删除这条记录<br>mysql  for update</li>
<li>zookeeper实现</li>
</ol>
<ul>
<li>排他锁<br><img src="https://img-blog.csdnimg.cn/img_convert/b98073366abb3f3b76e7ba6a1d9c94b8.png" alt="image.png"></li>
</ul>
<ul>
<li>共享锁（读锁）</li>
</ul>
<p>实现共享锁，使用java api的方式</p>
<p>命名服务</p>
<p>master选举</p>
<p>7*24小时可用， 99.999%可用</p>
<p>master-slave模式</p>
<p>使用zookeeper解决</p>
<h3 id="curator-提供应用场景的封装"><a href="#curator-提供应用场景的封装" class="headerlink" title="curator 提供应用场景的封装"></a>curator 提供应用场景的封装</h3><ul>
<li>curator-reciples </li>
<li>master/leader选举 </li>
<li>分布式锁（读锁、写锁）</li>
<li>分布式队列<br>…</li>
</ul>
<h3 id="LeaderLatch"><a href="#LeaderLatch" class="headerlink" title="LeaderLatch"></a>LeaderLatch</h3><p>写一个master<br>LeaderSelector<br>每一个应用都写一个临时有序节点，根据最小的节点来获得优先权</p>
<h3 id="zookeeper集群角色"><a href="#zookeeper集群角色" class="headerlink" title="zookeeper集群角色"></a>zookeeper集群角色</h3><p>leader<br>leader是zookeeper集群的核心</p>
<ol>
<li>事务请求的唯一调度者和处理者，保证集群事务处理的顺序性</li>
<li>集群内部各个服务器的调度者</li>
</ol>
<p>follower</p>
<ol>
<li>处理客户端非事务请求，以及转发事务请求给leader服务器</li>
<li>参与事务请求提议（proposal）的投票（客户端的一个事务请求，需要半数服务器投票通过以后才能通知leader commit;leader会发起一个提案，要求follower投票）</li>
<li>参与leader选举的投票</li>
</ol>
<p>observer</p>
<ul>
<li>观察zookeeper集群中最新状态的变化并将这些状态同步到observer服务器上</li>
<li>增加observer不影响集群中事务处理能力，同时还能提升集群的非事务处理能力</li>
</ul>
<h3 id="zookeeper的集群组成"><a href="#zookeeper的集群组成" class="headerlink" title="zookeeper的集群组成"></a>zookeeper的集群组成</h3><p>zookeeper一般是由2n+1台服务器 组成</p>
<h3 id="leader选举"><a href="#leader选举" class="headerlink" title="leader选举"></a>leader选举</h3>
      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="https://tomstand.gitee.io/2023/11/21/%E5%88%86%E5%B8%83%E5%BC%8F%E5%AD%98%E5%82%A8%E7%B3%BB%E7%BB%9F%E6%95%B0%E6%8D%AE%E5%BA%93%E4%B8%AD%E9%97%B4%E4%BB%B6-Mycat/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="tomstand">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="猿日记">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | 猿日记">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2023/11/21/%E5%88%86%E5%B8%83%E5%BC%8F%E5%AD%98%E5%82%A8%E7%B3%BB%E7%BB%9F%E6%95%B0%E6%8D%AE%E5%BA%93%E4%B8%AD%E9%97%B4%E4%BB%B6-Mycat/" class="post-title-link" itemprop="url">Untitled</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">Posted on</span>

      <time title="Created: 2023-11-21 20:16:19" itemprop="dateCreated datePublished" datetime="2023-11-21T20:16:19+08:00">2023-11-21</time>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h2 id="分布式存储系统数据库中间件-Mycat"><a href="#分布式存储系统数据库中间件-Mycat" class="headerlink" title="分布式存储系统数据库中间件-Mycat"></a>分布式存储系统数据库中间件-Mycat</h2><blockquote>
<p>官方文档网站：<a target="_blank" rel="noopener" href="http://mycat.org.cn/">http://mycat.org.cn/</a></p>
</blockquote>
<h3 id="Mycat基本定义"><a href="#Mycat基本定义" class="headerlink" title="Mycat基本定义"></a>Mycat基本定义</h3><ul>
<li><p>一个彻底开源的，面向企业应用开发的大数据库集群</p>
</li>
<li><p>支持事务、ACID、可以替代MySQL的加强版数据库</p>
</li>
<li><p>一个可以视为MySQL集群的企业级数据库，用来替代昂贵的Oracle集群</p>
</li>
<li><p>一个融合内存缓存技术、NoSQL技术、HDFS大数据的新型SQL Server</p>
</li>
<li><p>结合传统数据库和新型分布式数据仓库的新一代企业级数据库产品</p>
</li>
<li><p>一个新颖的数据库中间件产品</p>
</li>
</ul>
<h3 id="常用应用场景"><a href="#常用应用场景" class="headerlink" title="常用应用场景"></a>常用应用场景</h3><ul>
<li>单纯的读写分离，本身支持读写分离，主从切换</li>
<li>分库分表，对于超过1000万的表进行分片，最大支持1000亿的单表分片</li>
<li>多租户应用，每个应用一个库，但应用程序只是连接Mycat,从而不改造程序本身，实现多租户化</li>
<li>报表系统，借助于Mycat的分表能力，处理大规模报表的统计</li>
<li>替代Hbase，分析大数据</li>
<li>作为海量数据实时查询的一种简单有效方案，比如100亿条频繁查询的记录需要在3秒内查询出来结果，除了基于主键的查询，还可能存在范围查询或其他属性查询，此时Mycat可能是最简单有效的选择</li>
</ul>
<h3 id="数据库中间件基本架构图"><a href="#数据库中间件基本架构图" class="headerlink" title="数据库中间件基本架构图"></a>数据库中间件基本架构图</h3><p><img src="https://img-blog.csdnimg.cn/img_convert/33e2ccb66b426e362ecdfbd3732e526f.png" alt="image-20201223111136623"></p>
<p>中间件是介于数据库和应用之间，进行数据处理和交互的中间服务，能有效的管理数据底层的交互处理，让应用只需要集中在业务处理上，底层的数据让中间件来处理，提高整个系统的稳定性和扩展性</p>
<h3 id="MySQL读写分离方案及Mycat接入"><a href="#MySQL读写分离方案及Mycat接入" class="headerlink" title="MySQL读写分离方案及Mycat接入"></a>MySQL读写分离方案及Mycat接入</h3><p>标准的读写分离方案是主从模式，一个主节点负责数据写入，多个从节点负责数据读取，通常是1个主节点写入对应3个从节点读取数据</p>
<p><img src="https://img-blog.csdnimg.cn/img_convert/9183c694d845dd1e2579f3d801106fa5.png" alt="image-20201223143556191"></p>
<p>Mycat不负责mysql的数据同步问题，只负责sql读写分离资源的分开和管控</p>
<p><img src="https://img-blog.csdnimg.cn/img_convert/288e1dd7d70294e7e32c153f07f010db.png" alt="image-20201224110514629"></p>
<p>Mycat中执行以下两种配置即可接入mysql的主从配置</p>
<h3 id="Mysql高可用与集群方案及Mycat接入处理"><a href="#Mysql高可用与集群方案及Mycat接入处理" class="headerlink" title="Mysql高可用与集群方案及Mycat接入处理"></a>Mysql高可用与集群方案及Mycat接入处理</h3><p><img src="https://img-blog.csdnimg.cn/img_convert/6e6a27e9cfe863c83a577e9189d4d879.png" alt="image-20201224111127167"></p>
<p>对实时性要求不高的应用可以采用多台低配置的PC server扩展Slave的数量，将读压力分配到Slave上，解决数据库的读性能瓶颈，很多中小型网站采用此方式解决数据库压力</p>
<p><img src="https://img-blog.csdnimg.cn/img_convert/29608250be0c55dfbc6c31bc38ad3bf5.png" alt="image-20201224111455006"></p>
<p>MySQL Cluster使用不是很普遍，主要原因是配置管理负责，运维难度比较大并且存在安全问题</p>
<p><img src="https://img-blog.csdnimg.cn/img_convert/7469b5d7e842f0180ba39775290b20bc.png" alt="image-20201224111711196"></p>
<p>heartbeat是linux-HA工程的一个组件，核心包括两个：心跳检测和资源接管，指定时间内未收到对方发送的报文就认为是失败的，这是需要启动资源接管模块接管运行在对方主机上的资源服务</p>
<p><img src="https://img-blog.csdnimg.cn/img_convert/15befe90f3b2624df3f76d8497cbea2c.png" alt="image-20201224111900260"></p>
<p>PRBD是通过网络来实现设备的数据镜像同步的开源Cluster软件，它自动完成网络中两个不同服务器上的磁盘同步，相对于binlog日志同步，它是底层的磁盘同步，理论上是和很多文件系统的高可用配置</p>
<p><img src="https://img-blog.csdnimg.cn/img_convert/d381faf352b959c257295d5f59262c1b.png" alt="image-20201224112055633"></p>
<p>Lvs是一个虚拟的服务器集群系统，可以实现linux平台下的简单负载均衡，keepalived主要用于主机和备机的故障转移，是一种适用面很广的负载均衡和高可用方案，常用于Web系统上</p>
<p><img src="https://img-blog.csdnimg.cn/img_convert/72838db42b85338c210331950e996d0b.png" alt="image-20201224113102686"></p>
<p>Mycat的高可用官方推荐是采用Haproxt+keepalived的方式功能稳定性也比较好</p>
<h3 id="Mycat总体架构实现"><a href="#Mycat总体架构实现" class="headerlink" title="Mycat总体架构实现"></a>Mycat总体架构实现</h3><p><img src="https://img-blog.csdnimg.cn/img_convert/ebb32cfe04cc456d360b3a96b7affc56.png" alt="image-20201224122608348"></p>
<h3 id="Mycat网络IO架构与实现"><a href="#Mycat网络IO架构与实现" class="headerlink" title="Mycat网络IO架构与实现"></a>Mycat网络IO架构与实现</h3><p>Mycat同时实现了NIO和AIO，可以通过系统参数选择是使用AIO还是NIO</p>
<p><img src="https://img-blog.csdnimg.cn/img_convert/48bdf902353bf3c54b51b938188016b7.png" alt="image-20201224121631599"></p>
<p>核心包含四个处理时间：客户端连接服务端事件、服务端接受客户端连接事件、读事件、写事件</p>
<p><img src="https://img-blog.csdnimg.cn/img_convert/2e6ab58ed3aedad3a00da7a1bdcaeca0.png" alt="image-20201224121819682"></p>
<p>相比于AIO简单的架构，NIO应用场景比较复杂，在高性能IO框架中都会应用到NIO</p>
<p><img src="https://img-blog.csdnimg.cn/img_convert/fda149511aa0873e22f6a918f711fdda.png" alt="image-20201224122218833"></p>
<h3 id="Mycat最佳实践"><a href="#Mycat最佳实践" class="headerlink" title="Mycat最佳实践"></a>Mycat最佳实践</h3><p><img src="https://img-blog.csdnimg.cn/img_convert/cfc3b4c22d139a44b555a3ddaec7bfd0.png" alt="image-20201224120724654"></p>
<p>这种方案基本包含Mycat能满足和实现的全部实现方案</p>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="https://tomstand.gitee.io/2023/11/21/hello-world/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="tomstand">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="猿日记">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | 猿日记">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2023/11/21/hello-world/" class="post-title-link" itemprop="url">Hello World</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">Posted on</span>

      <time title="Created: 2023-11-21 20:16:19" itemprop="dateCreated datePublished" datetime="2023-11-21T20:16:19+08:00">2023-11-21</time>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <p>Welcome to <a target="_blank" rel="noopener" href="https://hexo.io/">Hexo</a>! This is your very first post. Check <a target="_blank" rel="noopener" href="https://hexo.io/docs/">documentation</a> for more info. If you get any problems when using Hexo, you can find the answer in <a target="_blank" rel="noopener" href="https://hexo.io/docs/troubleshooting.html">troubleshooting</a> or you can ask me on <a target="_blank" rel="noopener" href="https://github.com/hexojs/hexo/issues">GitHub</a>.</p>
<h2 id="Quick-Start"><a href="#Quick-Start" class="headerlink" title="Quick Start"></a>Quick Start</h2><h3 id="Create-a-new-post"><a href="#Create-a-new-post" class="headerlink" title="Create a new post"></a>Create a new post</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo new <span class="string">&quot;My New Post&quot;</span></span><br></pre></td></tr></table></figure>

<p>More info: <a target="_blank" rel="noopener" href="https://hexo.io/docs/writing.html">Writing</a></p>
<h3 id="Run-server"><a href="#Run-server" class="headerlink" title="Run server"></a>Run server</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo server</span><br></pre></td></tr></table></figure>

<p>More info: <a target="_blank" rel="noopener" href="https://hexo.io/docs/server.html">Server</a></p>
<h3 id="Generate-static-files"><a href="#Generate-static-files" class="headerlink" title="Generate static files"></a>Generate static files</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo generate</span><br></pre></td></tr></table></figure>

<p>More info: <a target="_blank" rel="noopener" href="https://hexo.io/docs/generating.html">Generating</a></p>
<h3 id="Deploy-to-remote-sites"><a href="#Deploy-to-remote-sites" class="headerlink" title="Deploy to remote sites"></a>Deploy to remote sites</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo deploy</span><br></pre></td></tr></table></figure>

<p>More info: <a target="_blank" rel="noopener" href="https://hexo.io/docs/one-command-deployment.html">Deployment</a></p>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="https://tomstand.gitee.io/2023/11/21/java%E7%BA%BF%E4%B8%8A%E9%97%AE%E9%A2%98%E5%AE%9A%E4%BD%8D%E7%A5%9E%E5%99%A8%20Arthas%E5%85%A5%E9%97%A8/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="tomstand">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="猿日记">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | 猿日记">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2023/11/21/java%E7%BA%BF%E4%B8%8A%E9%97%AE%E9%A2%98%E5%AE%9A%E4%BD%8D%E7%A5%9E%E5%99%A8%20Arthas%E5%85%A5%E9%97%A8/" class="post-title-link" itemprop="url">Untitled</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">Posted on</span>

      <time title="Created: 2023-11-21 20:16:19" itemprop="dateCreated datePublished" datetime="2023-11-21T20:16:19+08:00">2023-11-21</time>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h2 id="java线上问题定位神器-Arthas入门"><a href="#java线上问题定位神器-Arthas入门" class="headerlink" title="java线上问题定位神器 Arthas入门"></a>java线上问题定位神器 Arthas入门</h2><blockquote>
<p>Arthas（阿尔萨斯）是阿里巴巴开源的 Java 诊断工具，深受开发者喜爱。</p>
<p>当你遇到以下类似问题而束手无策时，Arthas 可以帮助你解决：</p>
<ol>
<li>这个类从哪个 jar 包加载的？为什么会报各种类相关的 Exception？</li>
<li>我改的代码为什么没有执行到？难道是我没 commit？分支搞错了？</li>
<li>遇到问题无法在线上 debug，难道只能通过加日志再重新发布吗？</li>
<li>线上遇到某个用户的数据处理有问题，但线上同样无法 debug，线下无法重现！</li>
<li>是否有一个全局视角来查看系统的运行状况？</li>
<li>有什么办法可以监控到JVM的实时运行状态？</li>
</ol>
<p>Arthas 采用命令行交互模式，同时提供丰富的 <code>Tab</code> 自动补全功能，进一步方便进行问题的定位和诊断。</p>
</blockquote>
<blockquote>
<p>阿里已经在github开源：<a target="_blank" rel="noopener" href="https://github.com/alibaba/arthas">https://github.com/alibaba/arthas</a></p>
<p>教程文档：<a target="_blank" rel="noopener" href="https://arthas.aliyun.com/doc/">https://arthas.aliyun.com/doc/</a></p>
</blockquote>
<ol>
<li><p>Arthas安装启动</p>
<p>在服务器下载<code>arthas-boot.jar</code>，然后用<code>java -jar</code>的方式启动：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">curl -O https://arthas.aliyun.com/arthas-boot.jar</span><br><span class="line">java -jar arthas-boot.jar</span><br></pre></td></tr></table></figure>

<p>启动的时候会检测当前运行的java服务id列表，选择需要检测的id后回车即可</p>
</li>
</ol>
<p><img src="https://img-blog.csdnimg.cn/img_convert/deb136fec2f7f5cdc7d2ffb8587ea19d.png" alt="image-20201211135027562"></p>
<ol start="2">
<li><p>查看dashboard，输入dashboard回车，可以展示仓前进程的信息，按<code>ctrl+c</code>可以中断执行。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">dashboard</span><br></pre></td></tr></table></figure></li>
</ol>
<p><img src="https://img-blog.csdnimg.cn/img_convert/1326e425de5ac6684ddc76b29493098a.png" alt="image-20201211140610688"></p>
<ol start="3">
<li><p>通过jad可以用来反编译项目中的类或者方法名</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">jad com.xxx.xxx.Application</span><br></pre></td></tr></table></figure>

<p><img src="https://img-blog.csdnimg.cn/img_convert/eabee97ab2f2e9126d2ac109e6633b52.png" alt="image-20201211141151436"></p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">jad com.xxx.xxx.Application run</span><br></pre></td></tr></table></figure>

<p><img src="https://img-blog.csdnimg.cn/img_convert/dbdd253039bfb0c4ad54ffcd3927e8e8.png" alt="image-20201211141537955"></p>
</li>
<li><p>退出arthas</p>
<p>如果只是退出当前的连接，可以用<code>quit</code>或者<code>exit</code>命令。Attach到目标进程上的arthas还会继续运行，端口会保持开放，下次连接时可以直接连接上。</p>
<p>如果想完全退出arthas，可以执行<code>stop</code>命令。</p>
</li>
</ol>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




  <nav class="pagination">
    <span class="page-number current">1</span><a class="page-number" href="/page/2/">2</a><a class="extend next" rel="next" href="/page/2/"><i class="fa fa-angle-right" aria-label="Next page"></i></a>
  </nav>

</div>
  </main>

  <footer class="footer">
    <div class="footer-inner">


<div class="copyright">
  &copy; 
  <span itemprop="copyrightYear">2023</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">tomstand</span>
</div>
  <div class="powered-by">Powered by <a href="https://hexo.io/" rel="noopener" target="_blank">Hexo</a> & <a href="https://theme-next.js.org/muse/" rel="noopener" target="_blank">NexT.Muse</a>
  </div>

    </div>
  </footer>

  
  <script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.1/anime.min.js" integrity="sha256-XL2inqUJaslATFnHdJOi9GfQ60on8Wx1C2H8DYiN1xY=" crossorigin="anonymous"></script>
<script src="/js/comments.js"></script><script src="/js/utils.js"></script><script src="/js/motion.js"></script><script src="/js/schemes/muse.js"></script><script src="/js/next-boot.js"></script>

  





  





</body>
</html>
